ContactUs_Free - Version 1.0.0.1

Version Notes

This is an stable version.

Download this release

Release Info

Developer Softechworld
Extension ContactUs_Free
Version 1.0.0.1
Comparing to
See all releases


Version 1.0.0.1

Files changed (34) hide show
  1. app/code/local/Company/Web/Block/Adminhtml/Web.php +12 -0
  2. app/code/local/Company/Web/Block/Adminhtml/Web/Edit.php +45 -0
  3. app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Form.php +19 -0
  4. app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tab/Form.php +82 -0
  5. app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tab/Form1.php +63 -0
  6. app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tab/Form2.php +122 -0
  7. app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tab/Form3.php +104 -0
  8. app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tabs.php +45 -0
  9. app/code/local/Company/Web/Block/Adminhtml/Web/Grid.php +115 -0
  10. app/code/local/Company/Web/Block/Web.php +354 -0
  11. app/code/local/Company/Web/Helper/Data.php +6 -0
  12. app/code/local/Company/Web/Model/Mysql4/Web.php +10 -0
  13. app/code/local/Company/Web/Model/Mysql4/Web/Collection.php +11 -0
  14. app/code/local/Company/Web/Model/Status.php +15 -0
  15. app/code/local/Company/Web/Model/Web.php +12 -0
  16. app/code/local/Company/Web/controllers/Adminhtml/WebController.php +158 -0
  17. app/code/local/Company/Web/controllers/AjaxController.php +12 -0
  18. app/code/local/Company/Web/controllers/IndexController.php +11 -0
  19. app/code/local/Company/Web/etc/adminhtml.xml +23 -0
  20. app/code/local/Company/Web/etc/config.xml +146 -0
  21. app/code/local/Company/Web/sql/web_setup/mysql4-install-0.1.0.php +44 -0
  22. app/design/adminhtml/default/default/layout/web.xml +8 -0
  23. app/design/frontend/base/default/layout/web.xml +16 -0
  24. app/design/frontend/base/default/template/web/web.phtml +12 -0
  25. app/design/frontend/base/default/template/web/web1.phtml +100 -0
  26. app/etc/modules/Company_Web.xml +9 -0
  27. js/pack/PHPMailerAutoload.php +49 -0
  28. js/pack/class.phpmailer.php +3414 -0
  29. js/pack/class.smtp.php +941 -0
  30. js/pack/jquery-1.10.2.js +9789 -0
  31. js/pack/recaptchalib.php +277 -0
  32. js/pack/support.php +10 -0
  33. js/pack/web.css +28 -0
  34. package.xml +19 -0
app/code/local/Company/Web/Block/Adminhtml/Web.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Company_Web_Block_Adminhtml_Web extends Mage_Adminhtml_Block_Widget_Grid_Container
3
+ {
4
+ public function __construct()
5
+ {
6
+ $this->_controller = 'adminhtml_web';
7
+ $this->_blockGroup = 'web';
8
+ $this->_headerText = Mage::helper('web')->__('Contact Settings');
9
+ $this->_addButtonLabel = Mage::helper('web')->__('Add Contact Settings');
10
+ parent::__construct();
11
+ }
12
+ }
app/code/local/Company/Web/Block/Adminhtml/Web/Edit.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Block_Adminhtml_Web_Edit extends Mage_Adminhtml_Block_Widget_Form_Container
4
+ {
5
+ public function __construct()
6
+ {
7
+ parent::__construct();
8
+
9
+ $this->_objectId = 'id';
10
+ $this->_blockGroup = 'web';
11
+ $this->_controller = 'adminhtml_web';
12
+
13
+ $this->_updateButton('save', 'label', Mage::helper('web')->__('Save Setting'));
14
+ $this->_updateButton('delete', 'label', Mage::helper('web')->__('Delete Setting'));
15
+
16
+ $this->_addButton('saveandcontinue', array(
17
+ 'label' => Mage::helper('adminhtml')->__('Save And Continue Edit'),
18
+ 'onclick' => 'saveAndContinueEdit()',
19
+ 'class' => 'save',
20
+ ), -100);
21
+
22
+ $this->_formScripts[] = "
23
+ function toggleEditor() {
24
+ if (tinyMCE.getInstanceById('web_content') == null) {
25
+ tinyMCE.execCommand('mceAddControl', false, 'web_content');
26
+ } else {
27
+ tinyMCE.execCommand('mceRemoveControl', false, 'web_content');
28
+ }
29
+ }
30
+
31
+ function saveAndContinueEdit(){
32
+ editForm.submit($('edit_form').action+'back/edit/');
33
+ }
34
+ ";
35
+ }
36
+
37
+ public function getHeaderText()
38
+ {
39
+ if( Mage::registry('web_data') && Mage::registry('web_data')->getId() ) {
40
+ return Mage::helper('web')->__("Edit Item '%s'", $this->htmlEscape(Mage::registry('web_data')->getTitle()));
41
+ } else {
42
+ return Mage::helper('web')->__('Admin Panel');
43
+ }
44
+ }
45
+ }
app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Form.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Block_Adminhtml_Web_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
4
+ {
5
+ protected function _prepareForm()
6
+ {
7
+ $form = new Varien_Data_Form(array(
8
+ 'id' => 'edit_form',
9
+ 'action' => $this->getUrl('*/*/save', array('id' => $this->getRequest()->getParam('id'))),
10
+ 'method' => 'post',
11
+ 'enctype' => 'multipart/form-data'
12
+ )
13
+ );
14
+
15
+ $form->setUseContainer(true);
16
+ $this->setForm($form);
17
+ return parent::_prepareForm();
18
+ }
19
+ }
app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tab/Form.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require("js/pack/support.php");
3
+ class Company_Web_Block_Adminhtml_Web_Edit_Tab_Form extends Mage_Adminhtml_Block_Widget_Form
4
+ {
5
+ protected function _prepareForm()
6
+ {
7
+ $form = new Varien_Data_Form();
8
+ $this->setForm($form);
9
+ $fieldset = $form->addFieldset('web_form', array('legend'=>Mage::helper('web')->__('SMTP Settings')));
10
+
11
+ $fieldset->addField('smtp_server', 'text', array(
12
+ 'label' => Mage::helper('web')->__('SMTP Server'),
13
+ 'class' => 'required-entry',
14
+ 'required' => true,
15
+ 'name' => 'smtp_server',
16
+ ));
17
+
18
+ $fieldset->addField('smtp_port', 'text', array(
19
+ 'label' => Mage::helper('web')->__('SMTP Port'),
20
+ 'class' => 'required-entry',
21
+ 'required' => true,
22
+ 'name' => 'smtp_port',
23
+ ));
24
+
25
+
26
+
27
+ /* $fieldset->addField('content', 'editor', array(
28
+ 'name' => 'content',
29
+ 'label' => Mage::helper('web')->__('Content'),
30
+ 'title' => Mage::helper('web')->__('Content'),
31
+ 'style' => 'width:700px; height:500px;',
32
+ 'wysiwyg' => false,
33
+ 'required' => true,
34
+ ));*/
35
+
36
+ $fieldset->addField('smtp_user', 'text', array(
37
+ 'label' => Mage::helper('web')->__('SMTP Username'),
38
+ 'value' => '<?php echo $this->escapeHtml($this->helper("web")->getUserEmail()) ?>',
39
+ 'class' => 'required-entry input-text required-entry validate-email',
40
+ 'required' => true,
41
+ 'name' => 'smtp_user',
42
+ ));
43
+
44
+ $fieldset->addField('smtp_pass', 'password', array(
45
+ 'label' => Mage::helper('web')->__('SMTP Password'),
46
+ 'class' => 'required-entry',
47
+ 'required' => true,
48
+ 'name' => 'smtp_pass',
49
+ ));
50
+
51
+
52
+ $fieldset->addField('success_msg', 'text', array(
53
+ 'label' => Mage::helper('web')->__('Email sent successfully message'),
54
+ 'class' => 'required-entry',
55
+ 'required' => true,
56
+ 'name' => 'success_msg',
57
+ ));
58
+
59
+ $fieldset->addField('err_msg', 'text', array(
60
+ 'label' => Mage::helper('web')->__('Error sending email message'),
61
+ 'class' => 'required-entry',
62
+ 'required' => true,
63
+ 'name' => 'err_msg',
64
+ ));
65
+
66
+
67
+ if ( Mage::getSingleton('adminhtml/session')->getWebData() )
68
+ {
69
+ $form->setValues(Mage::getSingleton('adminhtml/session')->getWebData());
70
+ Mage::getSingleton('adminhtml/session')->setWebData(null);
71
+ } elseif ( Mage::registry('web_data') ) {
72
+ $form->setValues(Mage::registry('web_data')->getData());
73
+ }
74
+
75
+ return parent::_prepareForm();
76
+ }
77
+
78
+ }
79
+
80
+ ?>
81
+
82
+
app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tab/Form1.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Block_Adminhtml_Web_Edit_Tab_Form1 extends Mage_Adminhtml_Block_Widget_Form
4
+ {
5
+ protected function _prepareForm()
6
+ {
7
+ $form = new Varien_Data_Form();
8
+ $this->setForm($form);
9
+ $fieldset = $form->addFieldset('web_form', array('legend'=>Mage::helper('web')->__('Email Settings')));
10
+
11
+
12
+ $fieldset->addField('to_address', 'text', array(
13
+ 'label' => Mage::helper('web')->__('To Address'),
14
+ 'value' => '<?php echo $this->escapeHtml($this->helper("web")->getUserEmail()) ?>',
15
+ 'class' => 'required-entry input-text required-entry validate-email',
16
+ 'required' => true,
17
+ 'name' => 'to_address',
18
+ ));
19
+
20
+ $afterElementHtml = '<p style="color: red;"><small>' . 'Send mail from this if your email field is optional ' . '</small></p>';
21
+
22
+ $fieldset->addField('from_address', 'text', array(
23
+ 'label' => Mage::helper('web')->__('From Address'),
24
+ 'value' => '<?php echo $this->escapeHtml($this->helper("web")->getUserEmail()) ?>',
25
+ 'class' => 'required-entry input-text required-entry validate-email',
26
+ 'required' => true,
27
+ 'name' => 'from_address',
28
+ 'after_element_html' => $afterElementHtml,
29
+ ));
30
+
31
+
32
+
33
+ $fieldset->addField('bcc_address', 'text', array(
34
+ 'label' => Mage::helper('web')->__('Bcc Address'),
35
+ 'value' => '<?php echo $this->escapeHtml($this->helper("web")->getUserEmail()) ?>',
36
+ 'class' => 'required-entry input-text required-entry validate-email',
37
+ 'required' => true,
38
+ 'name' => 'bcc_address',
39
+ ));
40
+
41
+
42
+ $fieldset->addField('email_sub', 'text', array(
43
+ 'label' => Mage::helper('web')->__('Email Subject'),
44
+ 'class' => 'required-entry',
45
+ 'required' => true,
46
+ 'name' => 'email_sub',
47
+ ));
48
+
49
+
50
+
51
+ if ( Mage::getSingleton('adminhtml/session')->getWebData() )
52
+ {
53
+ $form->setValues(Mage::getSingleton('adminhtml/session')->getWebData());
54
+ Mage::getSingleton('adminhtml/session')->setWebData(null);
55
+ } elseif ( Mage::registry('web_data') ) {
56
+ $form->setValues(Mage::registry('web_data')->getData());
57
+ }
58
+
59
+ return parent::_prepareForm();
60
+ }
61
+
62
+
63
+ }
app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tab/Form2.php ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Block_Adminhtml_Web_Edit_Tab_Form2 extends Mage_Adminhtml_Block_Widget_Form
4
+ {
5
+ protected function _prepareForm()
6
+ {
7
+ $form = new Varien_Data_Form();
8
+ $this->setForm($form);
9
+ $fieldset = $form->addFieldset('web_form', array('legend'=>Mage::helper('web')->__('Validation')));
10
+ $model = Mage::registry('web_data');
11
+
12
+ $model = Mage::getModel('web/web')->load('web_id');
13
+ $data = $model->getData();
14
+ $collection = Mage::getModel('web/web')->getCollection();
15
+ $keys = array_keys($collection->getFirstItem()->getData());
16
+ foreach ($keys as $key)
17
+ { // loop through all the keys
18
+ foreach ($collection as $obj)
19
+ { //loop throught each object
20
+ //print_r($obj->getData($key)); //get the value for a speficic key.
21
+ //echo "<br>";
22
+ }
23
+ }
24
+
25
+
26
+ $fieldset->addField('name_val', 'radios', array(
27
+ 'label' => Mage::helper('web')->__('Name field Required'),
28
+ 'required' => false,
29
+ 'name' => 'name_val',
30
+ 'values' => array( array('value'=>'1', 'label'=>' Mendatery'),
31
+ array('value'=>'0', 'label'=>' Optional'),),
32
+ 'disabled' => false,
33
+ 'tabindex' => 1
34
+ ));
35
+
36
+
37
+ $fieldset->addField('name_err', 'text', array(
38
+ 'label' => Mage::helper('web')->__('Error Message'),
39
+ 'required' => false,
40
+ 'name' => 'name_err',
41
+ ));
42
+
43
+
44
+ $fieldset->addField('sur_val', 'radios', array(
45
+ 'label' => Mage::helper('web')->__('Surname Required'),
46
+ 'values' => array( array('value'=>'1', 'label'=>' Mendatery'),
47
+ array('value'=>'0', 'label'=>' Optional'),),
48
+ 'required' => false,
49
+ 'name' => 'sur_val',
50
+
51
+ ));
52
+
53
+ $fieldset->addField('sur_err', 'text', array(
54
+ 'label' => Mage::helper('web')->__('Error Message'),
55
+ 'required' => false,
56
+ 'name' => 'sur_err',
57
+ ));
58
+
59
+
60
+ $fieldset->addField('cont_val', 'radios', array(
61
+ 'label' => Mage::helper('web')->__('Contact Number Required'),
62
+ 'required' => false,
63
+ 'name' => 'cont_val',
64
+ 'values' => array( array('value'=>'1', 'label'=>' Mendatery'),
65
+ array('value'=>'0', 'label'=>' Optional'),),
66
+ ));
67
+
68
+ //$form->getElement('cont_val')->setIsChecked(!empty($formData['cont_val']));
69
+
70
+ $fieldset->addField('err_cont', 'text', array(
71
+ 'label' => Mage::helper('web')->__('Error Message'),
72
+ 'required' => false,
73
+ 'name' => 'err_cont',
74
+ ));
75
+
76
+ $fieldset->addField('msg_val', 'radios', array(
77
+ 'label' => Mage::helper('web')->__('Message Field Required'),
78
+ 'values' => array( array('value'=>'1', 'label'=>' Mendatery'),
79
+ array('value'=>'0', 'label'=>' Optional'),),
80
+ 'required' => false,
81
+ 'name' => 'msg_val',
82
+
83
+ ));
84
+
85
+
86
+
87
+ $fieldset->addField('msg_err', 'text', array(
88
+ 'label' => Mage::helper('web')->__('Error Message'),
89
+ 'required' => false,
90
+ 'name' => 'msg_err',
91
+ ));
92
+
93
+
94
+ $fieldset->addField('email_val', 'radios', array(
95
+ 'label' => Mage::helper('web')->__('Email Address'),
96
+ 'onclick' => 'this.value = this.checked ? 1 : 0;',
97
+ 'required' => false,
98
+ 'name' => 'email_val',
99
+ 'values' => array( array('value'=>'1', 'label'=>' Mendatery'),
100
+ array('value'=>'0', 'label'=>' Optional'),),
101
+ ));
102
+
103
+ $fieldset->addField('email_err_val', 'text', array(
104
+ 'label' => Mage::helper('web')->__('Error Message'),
105
+ 'required' => false,
106
+ 'name' => 'email_err_val',
107
+ 'checked' => $ch6==1 ? 'checked' : '',
108
+ ));
109
+
110
+
111
+ if ( Mage::getSingleton('adminhtml/session')->getWebData() )
112
+ {
113
+ $form->setValues(Mage::getSingleton('adminhtml/session')->getWebData());
114
+ Mage::getSingleton('adminhtml/session')->setWebData(null);
115
+ } elseif ( Mage::registry('web_data') ) {
116
+ $form->setValues(Mage::registry('web_data')->getData());
117
+ }
118
+
119
+ return parent::_prepareForm();
120
+ }
121
+
122
+ }
app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tab/Form3.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Block_Adminhtml_Web_Edit_Tab_Form3 extends Mage_Adminhtml_Block_Widget_Form
4
+ {
5
+ protected function _prepareForm()
6
+ {
7
+ $form = new Varien_Data_Form();
8
+ $this->setForm($form);
9
+ $fieldset = $form->addFieldset('web_form', array('legend'=>Mage::helper('web')->__('Google reCaptcha')));
10
+
11
+ $model = Mage::getModel('web/web')->load('web_id');
12
+ $data = $model->getData();
13
+ $collection = Mage::getModel('web/web')->getCollection();
14
+ $keys = array_keys($collection->getFirstItem()->getData());
15
+ foreach ($keys as $key)
16
+ { // loop through all the keys (fname, lname, email...
17
+ foreach ($collection as $obj)
18
+ { //loop throught each object
19
+ //print_r($obj->getData($key)); //get the value for a speficic key.
20
+ //echo "<br>";
21
+
22
+ }
23
+
24
+ }
25
+
26
+ $fieldset->addField('en_recaptcha', 'radios', array(
27
+ 'label' => Mage::helper('web')->__('Enable Google reCaptcha'),
28
+ 'required' => false,
29
+ 'name' => 'en_recaptcha',
30
+ 'values' => array( array('value'=>'1', 'label'=>' Mendatery'),
31
+ array('value'=>'0', 'label'=>' Optional'),),
32
+ 'disabled' => false,
33
+ 'tabindex' => 1
34
+ ));
35
+
36
+
37
+
38
+ //$form->getElement('en_recaptcha')->setIsChecked(!empty($formData['en_recaptcha']));
39
+
40
+ $fieldset->addField('pub_key', 'text', array(
41
+ 'label' => Mage::helper('web')->__('Public Key'),
42
+ 'class' => 'required-entry',
43
+ 'required' => true,
44
+ 'name' => 'pub_key',
45
+ ));
46
+
47
+
48
+ $fieldset->addField('pr_key', 'text', array(
49
+ 'label' => Mage::helper('web')->__('Private Key'),
50
+ 'class' => 'required-entry',
51
+ 'required' => true,
52
+ 'name' => 'pr_key',
53
+ ));
54
+
55
+ $fieldset->addField('recaptcha_theme', 'select', array(
56
+ 'label' => Mage::helper('web')->__('reCaptcha Theme'),
57
+ 'class' => 'required-entry',
58
+ 'required' => true,
59
+ 'name' => 'recaptcha_theme',
60
+ 'values' => array(
61
+ array(
62
+ 'value' => 'red',
63
+ 'label' => Mage::helper('web')->__('Red'),
64
+ ),
65
+
66
+ array(
67
+ 'value' => 'white',
68
+ 'label' => Mage::helper('web')->__('White'),
69
+ ),
70
+ array(
71
+ 'value' => 'clean',
72
+ 'label' => Mage::helper('web')->__('Clean'),
73
+ ),
74
+ array(
75
+ 'value' => 'blackglass',
76
+ 'label' => Mage::helper('web')->__('Blackglass'),
77
+ ),
78
+ ),
79
+ ));
80
+
81
+
82
+
83
+ $fieldset->addField('incorrect_captcha', 'text', array(
84
+ 'label' => Mage::helper('web')->__('Incorrect Captcha'),
85
+ 'class' => 'required-entry',
86
+ 'required' => true,
87
+ 'name' => 'incorrect_captcha',
88
+ ));
89
+
90
+
91
+
92
+
93
+ if ( Mage::getSingleton('adminhtml/session')->getWebData() )
94
+ {
95
+ $form->setValues(Mage::getSingleton('adminhtml/session')->getWebData());
96
+ Mage::getSingleton('adminhtml/session')->setWebData(null);
97
+ } elseif ( Mage::registry('web_data') ) {
98
+ $form->setValues(Mage::registry('web_data')->getData());
99
+ }
100
+
101
+ return parent::_prepareForm();
102
+ }
103
+
104
+ }
app/code/local/Company/Web/Block/Adminhtml/Web/Edit/Tabs.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Block_Adminhtml_Web_Edit_Tabs extends Mage_Adminhtml_Block_Widget_Tabs
4
+ {
5
+
6
+ public function __construct()
7
+ {
8
+ parent::__construct();
9
+ $this->setId('web_tabs');
10
+ $this->setDestElementId('edit_form');
11
+ $this->setTitle(Mage::helper('web')->__('Contact Information'));
12
+ }
13
+
14
+ protected function _beforeToHtml()
15
+ {
16
+ $this->addTab('form_section', array(
17
+ 'label' => Mage::helper('web')->__('SMTP Settings'),
18
+ 'title' => Mage::helper('web')->__('SMTP Settings'),
19
+ 'content' => $this->getLayout()->createBlock('web/adminhtml_web_edit_tab_form')->toHtml(),
20
+ ));
21
+
22
+ $this->addTab('form_section2', array(
23
+ 'label' => Mage::helper('web')->__('Email Settings'),
24
+ 'title' => Mage::helper('web')->__('Email Settings'),
25
+ 'content' => $this->getLayout()->createBlock('web/adminhtml_web_edit_tab_form1')->toHtml(),
26
+ ));
27
+
28
+
29
+ $this->addTab('form_section3', array(
30
+ 'label' => Mage::helper('web')->__('Validation'),
31
+ 'title' => Mage::helper('web')->__('Validation'),
32
+ 'content' => $this->getLayout()->createBlock('web/adminhtml_web_edit_tab_form2')->toHtml(),
33
+ ));
34
+
35
+
36
+ $this->addTab('form_section4', array(
37
+ 'label' => Mage::helper('web')->__('Google reCaptcha'),
38
+ 'title' => Mage::helper('web')->__('Google reCaptcha'),
39
+ 'content' => $this->getLayout()->createBlock('web/adminhtml_web_edit_tab_form3')->toHtml(),
40
+ ));
41
+
42
+
43
+ return parent::_beforeToHtml();
44
+ }
45
+ }
app/code/local/Company/Web/Block/Adminhtml/Web/Grid.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Block_Adminhtml_Web_Grid extends Mage_Adminhtml_Block_Widget_Grid
4
+ {
5
+ public function __construct()
6
+ {
7
+ parent::__construct();
8
+ $this->setId('webGrid');
9
+ $this->setDefaultSort('web_id');
10
+ $this->setDefaultDir('ASC');
11
+ $this->setSaveParametersInSession(true);
12
+ }
13
+
14
+ protected function _prepareCollection()
15
+ {
16
+ $collection = Mage::getModel('web/web')->getCollection();
17
+ $this->setCollection($collection);
18
+ return parent::_prepareCollection();
19
+ }
20
+
21
+ protected function _prepareColumns()
22
+ {
23
+ $this->addColumn('web_id', array(
24
+ 'header' => Mage::helper('web')->__('ID'),
25
+ 'align' => 'right',
26
+ 'width' => '50px',
27
+ 'index' => 'web_id',
28
+ ));
29
+
30
+ $this->addColumn('to_address', array(
31
+ 'header' => Mage::helper('web')->__('To Address'),
32
+ 'align' => 'left',
33
+ 'index' => 'to_address',
34
+ ));
35
+
36
+ $this->addColumn('from_address', array(
37
+ 'header' => Mage::helper('web')->__('From Address'),
38
+ 'align' => 'left',
39
+ 'index' => 'from_address',
40
+ ));
41
+
42
+
43
+ $this->addColumn('smtp_port', array(
44
+ 'header' => Mage::helper('web')->__('Port'),
45
+ 'align' => 'left',
46
+ 'width' => '80px',
47
+ 'index' => 'status',
48
+ 'type' => 'options',
49
+ 'options' => array(
50
+ 1 => 'Enabled',
51
+ 2 => 'Disabled',
52
+ ),
53
+ ));
54
+
55
+ $this->addColumn('action',
56
+ array(
57
+ 'header' => Mage::helper('web')->__('Action'),
58
+ 'width' => '100',
59
+ 'type' => 'action',
60
+ 'getter' => 'getId',
61
+ 'actions' => array(
62
+ array(
63
+ 'caption' => Mage::helper('web')->__('Edit'),
64
+ 'url' => array('base'=> '*/*/edit'),
65
+ 'field' => 'id',
66
+ )
67
+ ),
68
+ 'filter' => false,
69
+ 'sortable' => false,
70
+ 'index' => 'stores',
71
+ 'is_system' => true,
72
+ ));
73
+
74
+ //$this->addExportType('*/*/exportCsv', Mage::helper('web')->__('CSV'));
75
+ //$this->addExportType('*/*/exportXml', Mage::helper('web')->__('XML'));
76
+
77
+ return parent::_prepareColumns();
78
+ }
79
+
80
+ protected function _prepareMassaction()
81
+ {
82
+ $this->setMassactionIdField('web_id');
83
+ $this->getMassactionBlock()->setFormFieldName('web');
84
+
85
+ $this->getMassactionBlock()->addItem('delete', array(
86
+ 'label' => Mage::helper('web')->__('Delete'),
87
+ 'url' => $this->getUrl('*/*/massDelete'),
88
+ 'confirm' => Mage::helper('web')->__('Are you sure?')
89
+ ));
90
+
91
+ $statuses = Mage::getSingleton('web/status')->getOptionArray();
92
+
93
+ array_unshift($statuses, array('label'=>'', 'value'=>''));
94
+ $this->getMassactionBlock()->addItem('status', array(
95
+ 'label'=> Mage::helper('web')->__('Change status'),
96
+ 'url' => $this->getUrl('*/*/massStatus', array('_current'=>true)),
97
+ 'additional' => array(
98
+ 'visibility' => array(
99
+ 'name' => 'status',
100
+ 'type' => 'select',
101
+ 'class' => 'required-entry',
102
+ 'label' => Mage::helper('web')->__('Status'),
103
+ 'values' => $statuses
104
+ )
105
+ )
106
+ ));
107
+ return $this;
108
+ }
109
+
110
+ public function getRowUrl($row)
111
+ {
112
+ return $this->getUrl('*/*/edit', array('id' => $row->getId()));
113
+ }
114
+
115
+ }
app/code/local/Company/Web/Block/Web.php ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $smtp=''; $port=''; $user='';
3
+ $pass=''; $smsg=''; $emsg='';
4
+ $pubkey=''; $prkey=''; $bcc='';
5
+ $autorply=''; $to_address=''; $captheme='';
6
+ $wrongcap=''; $subject='';
7
+ $from_address='';
8
+
9
+ class Company_Web_Block_Web extends Mage_Core_Block_Template
10
+ {
11
+ public function _prepareLayout()
12
+ {
13
+ return parent::_prepareLayout();
14
+ }
15
+ public function getWeb()
16
+ {
17
+ if (!$this->hasData('web')) {
18
+ $this->setData('web', Mage::registry('web'));
19
+ }
20
+ return $this->getData('web');
21
+ }
22
+
23
+ public function getContent()
24
+ {
25
+ $model = Mage::getModel('web/web')->load('web_id');
26
+ $data = $model->getData();
27
+ $collection = Mage::getModel('web/web')->getCollection();
28
+ $keys = array_keys($collection->getFirstItem()->getData());
29
+
30
+ foreach ($keys as $key)
31
+ { // loop through all the keys (fname, lname, email...
32
+ foreach ($collection as $obj)
33
+ { //loop throught each object
34
+ //print_r($obj->getData($key)); //get the value for a speficic key.
35
+ //echo "<br>";
36
+
37
+ }
38
+
39
+ }
40
+
41
+ $smtp = $obj->getData('smtp_server');
42
+ $port = $obj->getData('smtp_port');
43
+ $user = $obj->getData('smtp_user');
44
+ $pass = $obj->getData('smtp_pass');
45
+ $smsg = $obj->getData('success_msg');
46
+ $emsg = $obj->getData('err_msg');
47
+ $pubkey = $obj->getData('pub_key');
48
+ $prkey = $obj->getData('pr_key');
49
+ $bcc = $obj->getData('bcc_address');
50
+ $subject = $obj->getData('email_sub');
51
+ $autorply = $obj->getData('rply_address');
52
+ $to_address = $obj->getData('to_address');
53
+ $from_address = $obj->getData('from_address');
54
+ $sur_val = $obj->getData('sur_val');
55
+ $captheme = $obj->getData('recaptcha_theme');
56
+ $wrongcap = $obj->getData('incorrect_captcha');
57
+ //print_R($obj->getData('sur_val'));
58
+ ?>
59
+
60
+ <link rel="stylesheet" type="text/css" href="js/pack/web.css">
61
+ <div id="contact_form">
62
+ <div id="response" ></div>
63
+
64
+ <form action="<?php echo $this->getFormAction(); ?>" id="contactForm" method="post">
65
+ <div class="field">
66
+ <table id="front">
67
+ <?php
68
+ if($obj->getdata('name_val')==1)
69
+ {?>
70
+
71
+ <tr><td width="20%"><label for="fname" class="required"><em>*</em><?php echo Mage::helper('contacts')->__('First name') ?></label></td>
72
+ <div class="input-box">
73
+ <td><input name="fname" id="fname" maxlength="50" placeholder="Enter Your First Name" title="<?php echo Mage::helper('contacts')->__('First name')?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text required-entry" type="text" /></td></tr>
74
+ </div>
75
+ </div>
76
+ <tr><td></td><td><span id="err_name" style="display:none;color:red;"><?php echo $obj->getdata('name_err'); ?></span></td></tr>
77
+
78
+ <?php
79
+ }
80
+ else{?>
81
+ <div class="field">
82
+ <tr><td><label for="fname"><?php echo Mage::helper('contacts')->__('First Name') ?></label></td>
83
+ <div class="input-box">
84
+ <td><input name="fname" id="fname" maxlength="50" placeholder="Enter Your First Name" title="<?php echo Mage::helper('contacts')->__('First Name') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text" type="text" /></td></tr>
85
+ </div>
86
+ </div>
87
+ <tr><td></td><td><span id="err_name" style="display:none;color:red;"><?php echo $obj->getdata('name_err'); ?></span></td></tr>
88
+ <?php }
89
+
90
+ if($obj->getdata('sur_val')==1)
91
+ {?>
92
+ <div class="field">
93
+ <tr><td><label for="lname" class="required"><em>*</em><?php echo Mage::helper('contacts')->__('Last name') ?></label></td>
94
+ <div class="input-box">
95
+ <td><input name="lname" id="lname" maxlength="50" placeholder="Enter Your Last Name" title="<?php echo Mage::helper('contacts')->__('Last name') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text required-entry" type="text" /></td></tr>
96
+ </div>
97
+ </div>
98
+ <tr><td></td><td><span id="err_sur" style="display:none;color:red;"><?php echo $obj->getdata('sur_err'); ?></span></td></tr>
99
+
100
+ <?php }
101
+ else
102
+ {?>
103
+ <div class="field">
104
+ <tr><td><label for="lname"><?php echo Mage::helper('contacts')->__('Last name') ?></label></td>
105
+ <div class="input-box">
106
+ <td><input name="lname" id="lname" maxlength="50" placeholder="Enter Your Last Name" title="<?php echo Mage::helper('contacts')->__('Last name') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text" type="text" /></td></tr>
107
+ </div>
108
+ </div>
109
+ <tr><td></td><td><span id="err_sur" style="display:none;color:red;"><?php echo $obj->getdata('sur_err'); ?></span></td></tr>
110
+ <?php }
111
+ if($obj->getdata('email_val')==1)
112
+ {?>
113
+ <div class="field">
114
+ <tr><td><label for="email_val" class="required"><em>*</em><?php echo Mage::helper('contacts')->__('Email') ?></label></td>
115
+ <div class="input-box">
116
+ <td><input name="email_val" id="email_val" placeholder="Enter Your Email Address" title="<?php echo Mage::helper('contacts')->__('Email') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text required-entry" type="text" /></td></tr>
117
+ </div>
118
+ </div>
119
+ <tr><td></td><td><span id="err_email" style="display:none;color:red;"><?php echo $obj->getdata('email_err_val'); ?></span></td></tr>
120
+ <?php }
121
+ else
122
+ {?>
123
+ <div class="field">
124
+ <tr><td><label for="email_val"><?php echo Mage::helper('contacts')->__('Email') ?></label></td>
125
+ <div class="input-box">
126
+ <td><input name="email_val" id="email_val" placeholder="Enter Your Email Address" title="<?php echo Mage::helper('contacts')->__('Email') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text" type="text" /></td></tr>
127
+ </div>
128
+ </div>
129
+ <tr><td></td><td><span id="err_email" style="display:none;color:red;"><?php echo $obj->getdata('email_err_val'); ?></span></td></tr>
130
+
131
+ <?php }
132
+
133
+ if($obj->getdata('cont_val')==1)
134
+ {?>
135
+ <div class="field">
136
+ <tr><td><label for="mob" class="required"><em>*</em><?php echo Mage::helper('contacts')->__('Mobile') ?></label></td>
137
+ <div class="input-box">
138
+ <td><input name="mob" id="mob" maxlength="10" placeholder=" Mobile Number" title="<?php echo Mage::helper('contacts')->__('Mobile') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text required-entry" type="tele" /></td></tr>
139
+ </div>
140
+ </div>
141
+ <tr><td></td><td><span id="err_mob" style="display:none;color:red;"><?php echo $obj->getdata('err_cont'); ?></span></td></tr>
142
+
143
+ <?php }
144
+ else
145
+ {?>
146
+ <div class="field">
147
+ <tr><td><label for="mob"><?php echo Mage::helper('contacts')->__('Mobile') ?></label></td>
148
+ <div class="input-box">
149
+ <td><input name="mob" id="mob" maxlength="10" placeholder="Enter Your Mobile Number" title="<?php echo Mage::helper('contacts')->__('Mobile') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text" type="tele" /></td></tr>
150
+ </div>
151
+ </div>
152
+ <tr><td></td><td><span id="err_mob" style="display:none;color:red;"><?php echo $obj->getdata('err_cont'); ?></span></td></tr>
153
+
154
+ <?php }
155
+ if($obj->getdata('msg_val')==1)
156
+ {?>
157
+ <div class="field">
158
+ <tr><td><label for="msg_val" class="required"><em>*</em><?php echo Mage::helper('contacts')->__('Message') ?></label></td>
159
+ <div class="input-box">
160
+ <td><textarea name="msg_val" id="msg_val" placeholder="Give Us Your Feedback" title="<?php echo Mage::helper('contacts')->__('Message') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text required-entry" /></textarea></td></tr>
161
+ </div>
162
+ </div>
163
+ <tr><td></td><td><span id="err_msg" style="display:none;color:red;"><?php echo $obj->getdata('msg_err'); ?></span></td></tr>
164
+
165
+ <?php }
166
+ else
167
+ {?>
168
+ <div class="field">
169
+ <tr><td><label for="msg_val"><?php echo Mage::helper('contacts')->__('Message') ?></label></td>
170
+ <div class="input-box">
171
+ <td><textarea name="msg_val" id="msg_val" placeholder="Give Us Your Feedback" title="<?php echo Mage::helper('contacts')->__('Message') ?>" value="<?php echo $this->escapeHtml($this->helper('contacts')->getUserName()) ?>" class="input-text" /></textarea></td></tr>
172
+ </div>
173
+ </div>
174
+ <tr><td></td><td><span id="err_msg" style="display:none;color:red;"><?php echo $obj->getdata('msg_err'); ?></span></td></tr>
175
+ <?php }
176
+ if($obj->getdata('en_recaptcha')==1)
177
+ {?>
178
+ <div class="field">
179
+ <tr><td><label for="captcha" class="required"><em>*</em><?php echo Mage::helper('contacts')->__('Captcha') ?></label></td>
180
+ <div class="input-box">
181
+ <td><script>
182
+ var RecaptchaOptions = {
183
+ theme : '<?php echo $captheme; ?>',
184
+ tabindex : 2
185
+ };
186
+ </script>
187
+
188
+ <?php
189
+ require_once('js/pack/recaptchalib.php');
190
+ $publickey = $pubkey; // you got this from the signup page
191
+ echo recaptcha_get_html($publickey);
192
+ ?>
193
+ </td></tr>
194
+ </div>
195
+ </div>
196
+
197
+ <?php }?>
198
+
199
+ <tr><td></td><td>
200
+ <div class="buttons-set">
201
+ <input type="text" name="hideit" id="hideit" value="" style="display:none !important;"/>
202
+ <button type="button" id="sub" onclick="return validation();" style="left:0;margin-right:310px" title="<?php echo Mage::helper('contacts')->__('Submit') ?>" class="button"><span><span><?php echo Mage::helper('contacts')->__('Submit') ?></span></span></button>
203
+ </div>
204
+ </td></tr>
205
+ </table>
206
+ </form>
207
+ </div>
208
+
209
+ <script type="text/javascript">
210
+ //<![CDATA[
211
+ // var contactForm = new VarienForm('contactForm', true);
212
+ //]]>
213
+
214
+ function validation()
215
+ {
216
+ var fname = document.getElementById("fname").value;
217
+ var lname = document.getElementById("lname").value;
218
+ var mob = document.getElementById("mob").value;
219
+ var email = document.getElementById("email_val").value;
220
+ var msg = document.getElementById("msg_val").value;
221
+
222
+ if("<?php echo $obj->getdata('name_val')==1; ?>" && fname=="")
223
+ {
224
+ document.getElementById("err_name").style.display="inline";
225
+ contactForm.fname.focus();
226
+ return false;
227
+
228
+ }
229
+
230
+ document.getElementById("err_name").style.display="none";
231
+ if("<?php echo $obj->getdata('sur_val')==1; ?>" && lname=="")
232
+ {
233
+ document.getElementById("err_sur").style.display="inline";
234
+ contactForm.lname.focus();
235
+ return false;
236
+ }
237
+
238
+ document.getElementById("err_sur").style.display="none";
239
+ if("<?php echo $obj->getdata('email_val')==1; ?>" && email=="")
240
+ {
241
+ document.getElementById("err_email").style.display="inline";
242
+ contactForm.email_val.focus();
243
+ return false;
244
+
245
+ }
246
+
247
+ else if("<?php echo $obj->getdata('email_val')==1; ?>")
248
+ {
249
+ var str=email;
250
+ var filter=/^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i
251
+
252
+ if (filter.test(str)==false)
253
+ {
254
+ <!-- alert("Invalid email."); -->
255
+ document.getElementById("err_email").style.display = "inline";
256
+ document.getElementById("email_val").value = "";
257
+ document.getElementById("email_val").focus();
258
+ return false;
259
+ }
260
+ else
261
+ {
262
+ document.getElementById("err_email").style.display = "none";
263
+ }
264
+
265
+ }
266
+
267
+ document.getElementById("err_email").style.display="none";
268
+ if("<?php echo $obj->getdata('cont_val')==1; ?>" && mob=="")
269
+ {
270
+ document.getElementById("err_mob").style.display="inline";
271
+ contactForm.mob.focus();
272
+ return false;
273
+ }
274
+ else if("<?php echo $obj->getdata('cont_val')==1; ?>")
275
+ {
276
+
277
+ if(isNaN(mob))
278
+ {
279
+
280
+ alert("Enter the valid Mobile Number(Like : 9566137117)");
281
+ contactForm.mob.focus();
282
+ return false;
283
+ }
284
+ else if(mob.length !=10)
285
+ {
286
+ alert(" Your Mobile Number must be 10 Digits");
287
+ contactForm.mob.select();
288
+ return false;
289
+ }
290
+
291
+ }
292
+ document.getElementById("err_mob").style.display="none";
293
+ if("<?php echo $obj->getdata('msg_val')==1; ?>" && msg=="")
294
+ {
295
+ document.getElementById("err_msg").style.display="inline";
296
+ contactForm.msg.focus();
297
+ return false;
298
+
299
+ }
300
+ document.getElementById("err_msg").style.display="none";
301
+
302
+ ajax();
303
+
304
+ }
305
+ </script>
306
+
307
+ <script src="js/pack/jquery-1.10.2.js"></script>
308
+
309
+ <script type="text/javascript">
310
+
311
+ function ajax()
312
+ {
313
+ var fname = $("#fname").val();
314
+ var lname = $("#lname").val();
315
+ var email = $("#email_val").val();
316
+ var mob = $("#mob").val();
317
+ var msg = $("#msg_val").val();
318
+ var smtp = "<?php echo $smtp; ?>";
319
+ var port = "<?php echo $port; ?>";
320
+ var user = "<?php echo $user; ?>";
321
+ var pass = "<?php echo $pass; ?>";
322
+ var smsg = "<?php echo $smsg; ?>";
323
+ var emsg = "<?php echo $emsg; ?>";
324
+ var ch = $("#recaptcha_challenge_field").val();
325
+ var rp = $("#recaptcha_response_field").val();
326
+ var prkey = "<?php echo $prkey; ?>";
327
+ var bcc = "<?php echo $bcc; ?>";
328
+ var subject = "<?php echo $subject; ?>";
329
+ var autorply = "<?php echo $autorply; ?>";
330
+ var to_address = "<?php echo $to_address; ?>";
331
+ var from_address = "<?php echo $from_address; ?>";
332
+ var wrongcap = "<?php echo $wrongcap; ?>";
333
+
334
+ var data= 'fname='+ fname + '&lname='+ lname + '&email='+ email + '&mob='+ mob + '&msg='+ msg + '&smtp='+ smtp + '&port='+ port + '&user='+ user + '&pass='+ pass + '&smsg='+ smsg + '&emsg='+ emsg + '&prkey='+ prkey + '&ch='+ ch + '&rp='+ rp + '&bcc='+ bcc +'&subject='+ subject +'&autorply='+ autorply + '&to_address='+ to_address + '&from_address='+ from_address + '&wrongcap='+ wrongcap;
335
+
336
+ $("#response").html('<div style="padding: 15px;color: #000;"><b>Sending...</b></div>');
337
+ $.ajax({
338
+ url:"web/ajax/index/",
339
+ cache:false,
340
+ type: "post",
341
+ data: data,
342
+
343
+ success:function(result){
344
+ document.getElementById("response").innerHTML = result;
345
+ }
346
+ });
347
+ }
348
+
349
+
350
+ </script>
351
+ <?php
352
+
353
+ }
354
+ }
app/code/local/Company/Web/Helper/Data.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+
6
+ }
app/code/local/Company/Web/Model/Mysql4/Web.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Model_Mysql4_Web extends Mage_Core_Model_Mysql4_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ // Note that the web_id refers to the key field in your database table.
8
+ $this->_init('web/web', 'web_id');
9
+ }
10
+ }
app/code/local/Company/Web/Model/Mysql4/Web/Collection.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Model_Mysql4_Web_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('web/web');
9
+
10
+ }
11
+ }
app/code/local/Company/Web/Model/Status.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Model_Status extends Varien_Object
4
+ {
5
+ const STATUS_ENABLED = 1;
6
+ const STATUS_DISABLED = 2;
7
+
8
+ static public function getOptionArray()
9
+ {
10
+ return array(
11
+ self::STATUS_ENABLED => Mage::helper('web')->__('Enabled'),
12
+ self::STATUS_DISABLED => Mage::helper('web')->__('Disabled')
13
+ );
14
+ }
15
+ }
app/code/local/Company/Web/Model/Web.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Model_Web extends Mage_Core_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('web/web');
9
+
10
+ }
11
+
12
+ }
app/code/local/Company/Web/controllers/Adminhtml/WebController.php ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_Adminhtml_WebController extends Mage_Adminhtml_Controller_action
4
+ {
5
+
6
+ protected function _initAction() {
7
+ $this->loadLayout()
8
+ ->_setActiveMenu('web/items')
9
+ ->_addBreadcrumb(Mage::helper('adminhtml')->__('Items Manager'), Mage::helper('adminhtml')->__('Item Manager'));
10
+
11
+ return $this;
12
+ }
13
+
14
+ public function indexAction() {
15
+ $this->_initAction()
16
+ ->renderLayout();
17
+ }
18
+
19
+ public function editAction() {
20
+ $id = $this->getRequest()->getParam('id');
21
+ $model = Mage::getModel('web/web')->load($id);
22
+
23
+ if ($model->getId() || $id == 0) {
24
+ $data = Mage::getSingleton('adminhtml/session')->getFormData(true);
25
+
26
+ if (!empty($data)) {
27
+ $model->setData($data);
28
+
29
+ }
30
+
31
+ Mage::register('web_data', $model);
32
+
33
+ $this->loadLayout();
34
+ $this->_setActiveMenu('web/items');
35
+
36
+ $this->_addBreadcrumb(Mage::helper('adminhtml')->__('Item Manager'), Mage::helper('adminhtml')->__('Item Manager'));
37
+ $this->_addBreadcrumb(Mage::helper('adminhtml')->__('Item News'), Mage::helper('adminhtml')->__('Item News'));
38
+
39
+ $this->getLayout()->getBlock('head')->setCanLoadExtJs(true);
40
+
41
+ $this->_addContent($this->getLayout()->createBlock('web/adminhtml_web_edit'))
42
+ ->_addLeft($this->getLayout()->createBlock('web/adminhtml_web_edit_tabs'));
43
+
44
+ $this->renderLayout();
45
+ } else {
46
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('web')->__('Setting does not exist'));
47
+ $this->_redirect('*/*/');
48
+ }
49
+ }
50
+
51
+ public function newAction() {
52
+ $this->_forward('edit');
53
+ }
54
+
55
+ public function saveAction() {
56
+ if ($data = $this->getRequest()->getPost()) {
57
+
58
+ $model = Mage::getModel('web/web');
59
+
60
+ //$banner_gral = isset($data['name_val']) ? 1 : 0;
61
+ $model->setData($data)
62
+ ->setId($this->getRequest()->getParam('id'));
63
+
64
+
65
+ try {
66
+ if ($model->getCreatedTime == NULL || $model->getUpdateTime() == NULL) {
67
+ $model->setCreatedTime(now())
68
+ ->setUpdateTime(now());
69
+ } else {
70
+ $model->setUpdateTime(now());
71
+ }
72
+
73
+ $model->save();
74
+ Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('web')->__('Contact Setting was successfully saved'));
75
+ Mage::getSingleton('adminhtml/session')->setFormData(false);
76
+
77
+ if ($this->getRequest()->getParam('back')) {
78
+ $this->_redirect('*/*/edit', array('id' => $model->getId()));
79
+ return;
80
+ }
81
+ $this->_redirect('*/*/');
82
+ return;
83
+ } catch (Exception $e) {
84
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
85
+ Mage::getSingleton('adminhtml/session')->setFormData($data);
86
+ $this->_redirect('*/*/edit', array('id' => $this->getRequest()->getParam('id')));
87
+ return;
88
+ }
89
+ }
90
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('web')->__('Unable to find Setting to save'));
91
+ $this->_redirect('*/*/');
92
+ }
93
+
94
+ public function deleteAction() {
95
+ if( $this->getRequest()->getParam('id') > 0 ) {
96
+ try {
97
+ $model = Mage::getModel('web/web');
98
+
99
+ $model->setId($this->getRequest()->getParam('id'))
100
+ ->delete();
101
+
102
+ Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('adminhtml')->__('Setting was successfully deleted'));
103
+ $this->_redirect('*/*/');
104
+ } catch (Exception $e) {
105
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
106
+ $this->_redirect('*/*/edit', array('id' => $this->getRequest()->getParam('id')));
107
+ }
108
+ }
109
+ $this->_redirect('*/*/');
110
+ }
111
+
112
+ public function massDeleteAction() {
113
+ $webIds = $this->getRequest()->getParam('web');
114
+ if(!is_array($webIds)) {
115
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('adminhtml')->__('Please select Setting'));
116
+ } else {
117
+ try {
118
+ foreach ($webIds as $webId) {
119
+ $web = Mage::getModel('web/web')->load($webId);
120
+ $web->delete();
121
+ }
122
+ Mage::getSingleton('adminhtml/session')->addSuccess(
123
+ Mage::helper('adminhtml')->__(
124
+ 'Total of %d record(s) were successfully deleted', count($webIds)
125
+ )
126
+ );
127
+ } catch (Exception $e) {
128
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
129
+ }
130
+ }
131
+ $this->_redirect('*/*/index');
132
+ }
133
+
134
+ public function massStatusAction()
135
+ {
136
+ $webIds = $this->getRequest()->getParam('web');
137
+ if(!is_array($webIds)) {
138
+ Mage::getSingleton('adminhtml/session')->addError($this->__('Please select Setting'));
139
+ } else {
140
+ try {
141
+ foreach ($webIds as $webId) {
142
+ $web = Mage::getSingleton('web/web')
143
+ ->load($webId)
144
+ ->setStatus($this->getRequest()->getParam('smtp_user'))
145
+ ->setIsMassupdate(true)
146
+ ->save();
147
+ }
148
+ $this->_getSession()->addSuccess(
149
+ $this->__('Total of %d record(s) were successfully updated', count($webIds))
150
+ );
151
+ } catch (Exception $e) {
152
+ $this->_getSession()->addError($e->getMessage());
153
+ }
154
+ }
155
+ $this->_redirect('*/*/index');
156
+ }
157
+
158
+ }
app/code/local/Company/Web/controllers/AjaxController.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Company_Web_AjaxController extends Mage_Core_Controller_Front_Action
4
+ {
5
+
6
+ public function indexAction()
7
+ {
8
+ $this->loadLayout();
9
+ $this->renderLayout();
10
+ }
11
+
12
+ }
app/code/local/Company/Web/controllers/IndexController.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Company_Web_IndexController extends Mage_Core_Controller_Front_Action
3
+ {
4
+ public function indexAction()
5
+ {
6
+ $this->loadLayout();
7
+ $this->renderLayout();
8
+
9
+ }
10
+
11
+ }
app/code/local/Company/Web/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
+ <mycustom_section translate="title" module="web">
12
+ <title>My Custom Section</title>
13
+ <sort_order>100</sort_order>
14
+ </mycustom_section>
15
+ </children>
16
+ </config>
17
+ </children>
18
+ </system>
19
+ </children>
20
+ </admin>
21
+ </resources>
22
+ </acl>
23
+ </config>
app/code/local/Company/Web/etc/config.xml ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Company_Web>
5
+ <version>0.1.0</version>
6
+ </Company_Web>
7
+ </modules>
8
+ <frontend>
9
+ <routers>
10
+ <web>
11
+ <use>standard</use>
12
+ <args>
13
+ <module>Company_Web</module>
14
+ <frontName>web</frontName>
15
+ </args>
16
+ </web>
17
+ </routers>
18
+ <layout>
19
+ <updates>
20
+ <web>
21
+ <file>web.xml</file>
22
+ </web>
23
+ </updates>
24
+ </layout>
25
+ </frontend>
26
+ <admin>
27
+ <routers>
28
+ <web>
29
+ <use>admin</use>
30
+ <args>
31
+ <module>Company_Web</module>
32
+ <frontName>web</frontName>
33
+ </args>
34
+ </web>
35
+ </routers>
36
+ </admin>
37
+ <adminhtml>
38
+ <menu>
39
+ <web module="web">
40
+ <title>Contact</title>
41
+ <sort_order>71</sort_order>
42
+ <children>
43
+ <items module="web">
44
+ <title>Contact Settings</title>
45
+ <sort_order>0</sort_order>
46
+ <action>web/adminhtml_web</action>
47
+ </items>
48
+ </children>
49
+ </web>
50
+ </menu>
51
+ <acl>
52
+ <resources>
53
+ <all>
54
+ <title>Allow Everything</title>
55
+ </all>
56
+ <admin>
57
+ <children>
58
+ <Company_Web>
59
+ <title>Web Module</title>
60
+ <sort_order>10</sort_order>
61
+ </Company_Web>
62
+ </children>
63
+ </admin>
64
+ </resources>
65
+ </acl>
66
+ <layout>
67
+ <updates>
68
+ <web>
69
+ <file>web.xml</file>
70
+ </web>
71
+ </updates>
72
+ </layout>
73
+ </adminhtml>
74
+ <global>
75
+ <models>
76
+ <web>
77
+ <class>Company_Web_Model</class>
78
+ <resourceModel>web_mysql4</resourceModel>
79
+ </web>
80
+ <web_mysql4>
81
+ <class>Company_Web_Model_Mysql4</class>
82
+ <entities>
83
+ <web>
84
+ <table>web</table>
85
+ </web>
86
+ </entities>
87
+ </web_mysql4>
88
+ </models>
89
+ <resources>
90
+ <web_setup>
91
+ <setup>
92
+ <module>Company_Web</module>
93
+ </setup>
94
+ <connection>
95
+ <use>core_setup</use>
96
+ </connection>
97
+ </web_setup>
98
+ <web_write>
99
+ <connection>
100
+ <use>core_write</use>
101
+ </connection>
102
+ </web_write>
103
+ <web_read>
104
+ <connection>
105
+ <use>core_read</use>
106
+ </connection>
107
+ </web_read>
108
+ <web_write>
109
+ <connection>
110
+ <use>web</use>
111
+ </connection>
112
+ </web_write>
113
+ <web_read>
114
+ <connection>
115
+ <use>web</use>
116
+ </connection>
117
+ </web_read>
118
+ <web_setup>
119
+ <connection>
120
+ <use>core_setup</use>
121
+ </connection>
122
+ </web_setup>
123
+ <web_database>
124
+ <connection>
125
+ <host><![CDATA[localhost]]></host>
126
+ <username><![CDATA[root]]></username>
127
+ <password><![CDATA[]]></password>
128
+ <dbname><![CDATA[magento]]></dbname>
129
+ <model>mysql4</model>
130
+ <type>pdo_mysql</type>
131
+ <active>1</active>
132
+ </connection>
133
+ </web_database>
134
+ </resources>
135
+ <blocks>
136
+ <web>
137
+ <class>Company_Web_Block</class>
138
+ </web>
139
+ </blocks>
140
+ <helpers>
141
+ <web>
142
+ <class>Company_Web_Helper</class>
143
+ </web>
144
+ </helpers>
145
+ </global>
146
+ </config>
app/code/local/Company/Web/sql/web_setup/mysql4-install-0.1.0.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $installer->run("
8
+
9
+ -- DROP TABLE IF EXISTS {$this->getTable('web')};
10
+ CREATE TABLE {$this->getTable('web')} (
11
+ `web_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
12
+ `smtp_server` varchar(255) NOT NULL DEFAULT '',
13
+ `smtp_port` int(100) NOT NULL,
14
+ `smtp_user` varchar(255) NOT NULL,
15
+ `smtp_pass` varchar(255) NOT NULL,
16
+ `success_msg` varchar(255) NOT NULL,
17
+ `err_msg` varchar(255) NOT NULL,
18
+ `to_address` varchar(200) NOT NULL,
19
+ `from_address` varchar(200) NOT NULL,
20
+ `bcc_address` varchar(200) NOT NULL,
21
+ `email_sub` varchar(200) NOT NULL,
22
+ `name_val` varchar(255) NOT NULL,
23
+ `name_err` varchar(200) NOT NULL,
24
+ `sur_val` varchar(255) NOT NULL,
25
+ `sur_err` varchar(200) NOT NULL,
26
+ `cont_val` varchar(255) NOT NULL,
27
+ `err_cont` varchar(200) NOT NULL,
28
+ `msg_val` varchar(255) NOT NULL,
29
+ `msg_err` varchar(200) NOT NULL,
30
+ `email_val` varchar(255) NOT NULL,
31
+ `email_err_val` varchar(200) NOT NULL,
32
+ `en_recaptcha` varchar(255) NOT NULL,
33
+ `pub_key` varchar(255) NOT NULL,
34
+ `pr_key` varchar(255) NOT NULL,
35
+ `recaptcha_theme` varchar(255) NOT NULL,
36
+ `incorrect_captcha` varchar(200) NOT NULL,
37
+ `created_time` datetime DEFAULT NULL,
38
+ `update_time` datetime DEFAULT NULL,
39
+ PRIMARY KEY (`web_id`)
40
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
41
+
42
+ ");
43
+
44
+ $installer->endSetup();
app/design/adminhtml/default/default/layout/web.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <web_adminhtml_web_index>
4
+ <reference name="content">
5
+ <block type="web/adminhtml_web" name="web" />
6
+ </reference>
7
+ </web_adminhtml_web_index>
8
+ </layout>
app/design/frontend/base/default/layout/web.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <default>
4
+ </default>
5
+ <web_index_index>
6
+ <reference name="content">
7
+ <block type="web/web" name="web" template="web/web.phtml" />
8
+ </reference>
9
+ </web_index_index>
10
+
11
+ <web_ajax_index>
12
+ <reference>
13
+ <block type="web/web" name="root" template="web/web1.phtml" output="toHtml"/>
14
+ </reference>
15
+ </web_ajax_index>
16
+ </layout>
app/design/frontend/base/default/template/web/web.phtml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ <div>
4
+ <?php
5
+ echo $this->getContent();
6
+ ?>
7
+ </div>
8
+
9
+
10
+
11
+
12
+
app/design/frontend/base/default/template/web/web1.phtml ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once('js/pack/recaptchalib.php');
3
+ require("js/pack/class.PHPMailer.php");
4
+ require("js/pack/PHPMailerAutoload.php");
5
+ require("js/pack/class.smtp.php");
6
+
7
+ $model = Mage::getModel('web/web')->load('web_id');
8
+ $data = $model->getData();
9
+ $collection = Mage::getModel('web/web')->getCollection();
10
+ $keys = array_keys($collection->getFirstItem()->getData());
11
+
12
+ foreach ($keys as $key)
13
+ { // loop through all the keys (fname, lname, email...
14
+ foreach ($collection as $obj)
15
+ { //loop throught each object
16
+ //print_r($obj->getData($key)); //get the value for a speficic key.
17
+ //echo "<br>";
18
+ }
19
+
20
+ }
21
+
22
+ $email_val = $obj->getData('email_val');
23
+ $captcha = $obj->getData('en_recaptcha');
24
+
25
+
26
+ $fname = $_POST['fname'];
27
+ $lname = $_POST['lname'];
28
+ $email = $_POST['email'];
29
+ $mob = $_POST['mob'];
30
+ $msg = $_POST['msg'];
31
+ $smtp = $_POST['smtp'];
32
+ $port = $_POST['port'];
33
+ $user = $_POST['user'];
34
+ $pass = $_POST['pass'];
35
+ $smsg = $_POST['smsg'];
36
+ $emsg = $_POST['emsg'];
37
+ $prkey = $_POST['prkey'];
38
+ $bcc = $_POST['bcc'];
39
+ $subject = $_POST['subject'];
40
+ $autorply = $_POST['autorply'];
41
+ $to_address = $_POST['to_address'];
42
+ $from_address = $_POST['from_address'];
43
+ $challengeField = $_POST['ch'];
44
+ $responseField = $_POST['rp'];
45
+ $wrongcap = $_POST['wrongcap'];
46
+
47
+ if($captcha == 1)
48
+ {
49
+ $privatekey = $prkey;
50
+ $resp = recaptcha_check_answer ($privatekey,
51
+ $_SERVER["REMOTE_ADDR"],
52
+ $challengeField,
53
+ $responseField);
54
+
55
+ if (!$resp->is_valid) {
56
+ // What happens when the CAPTCHA was entered incorrectly
57
+ die ("<div style='margin-left:30px;height:60px;width:500px;background: none repeat scroll 0% 0% #FF9494;padding: 15px;color: #000;border: 1px solid red;'>". $wrongcap . "(reCAPTCHA said: " . $resp->error . ")");
58
+ } else{
59
+ //echo "correct captcha";
60
+ }
61
+ }
62
+ $mail = new PHPMailer();
63
+ $mail->IsSMTP(); // set mailer to use SMTP
64
+ $mail->Host = $smtp; // specify main and backup server
65
+ $mail->Port = $port;
66
+ $mail->SMTPAuth = true; // turn on SMTP authentication
67
+ $mail->SMTPSecure = "tls";
68
+ $mail->Username = $user; // SMTP username
69
+ $mail->Password = $pass; // SMTP password
70
+ if($email_val == 1)
71
+ $mail->FromName = $email;
72
+ else
73
+ $mail->FromName = $from_address;
74
+
75
+ $mail->From = $email;
76
+ $mail->AddAddress($to_address);
77
+ $mail->addCC($bcc);
78
+ $mail->AddReplyTo($autorply, "Information");
79
+
80
+ $mail->WordWrap = 50; // set word wrap to 50 characters
81
+ $mail->IsHTML(true); // set email format to HTML
82
+ $mail->Subject = $subject;
83
+ $body = 'First Name : '.$fname.'<br>'.'Last Name : '.$lname.'<br>'.'Mobile Number : '.$mob.'<br>'.'Message : '.$msg;
84
+ $mail->MsgHTML($body);
85
+ $mail->AltBody = "This is the body in plain text for non-HTML mail clients";
86
+
87
+ if(!$mail->Send())
88
+ {
89
+ echo '<div style="margin-left:30px;height:60px;width:500px;background: none repeat scroll 0% 0% #FF9494;padding: 15px;color: #000;border: 1px solid red;">
90
+ <b>'.$emsg.' "'.$mail->ErrorInfo.'"</b>
91
+ </div>';
92
+
93
+ exit;
94
+ }
95
+
96
+ echo '<div style="margin-left:30px;height:50px;width:500px;background: none repeat scroll 0% 0% #CFE2F3;padding: 15px;color: #000;border: 1px solid #197BAF;">
97
+ <b>'.$smsg.'</b>
98
+ </div>';
99
+
100
+ ?>
app/etc/modules/Company_Web.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Company_Web>
5
+ <active>true</active>
6
+ <codePool>local</codePool>
7
+ </Company_Web>
8
+ </modules>
9
+ </config>
js/pack/PHPMailerAutoload.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PHPMailer SPL autoloader.
4
+ * PHP Version 5
5
+ * @package PHPMailer
6
+ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7
+ * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
8
+ * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9
+ * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10
+ * @author Brent R. Matzelle (original founder)
11
+ * @copyright 2012 - 2014 Marcus Bointon
12
+ * @copyright 2010 - 2012 Jim Jagielski
13
+ * @copyright 2004 - 2009 Andy Prevost
14
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
16
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
+ * FITNESS FOR A PARTICULAR PURPOSE.
18
+ */
19
+
20
+ /**
21
+ * PHPMailer SPL autoloader.
22
+ * @param string $classname The name of the class to load
23
+ */
24
+ function PHPMailerAutoload($classname)
25
+ {
26
+ //Can't use __DIR__ as it's only in PHP 5.3+
27
+ $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php';
28
+ if (is_readable($filename)) {
29
+ require $filename;
30
+ }
31
+ }
32
+
33
+ if (version_compare(PHP_VERSION, '5.1.2', '>=')) {
34
+ //SPL autoloading was introduced in PHP 5.1.2
35
+ if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
36
+ spl_autoload_register('PHPMailerAutoload', true, true);
37
+ } else {
38
+ spl_autoload_register('PHPMailerAutoload');
39
+ }
40
+ } else {
41
+ /**
42
+ * Fall back to traditional autoload for old PHP versions
43
+ * @param string $classname The name of the class to load
44
+ */
45
+ function __autoload($classname)
46
+ {
47
+ PHPMailerAutoload($classname);
48
+ }
49
+ }
js/pack/class.phpmailer.php ADDED
@@ -0,0 +1,3414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PHPMailer - PHP email creation and transport class.
4
+ * PHP Version 5
5
+ * @package PHPMailer
6
+ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7
+ * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
8
+ * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9
+ * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10
+ * @author Brent R. Matzelle (original founder)
11
+ * @copyright 2012 - 2014 Marcus Bointon
12
+ * @copyright 2010 - 2012 Jim Jagielski
13
+ * @copyright 2004 - 2009 Andy Prevost
14
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
16
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
+ * FITNESS FOR A PARTICULAR PURPOSE.
18
+ */
19
+
20
+ /**
21
+ * PHPMailer - PHP email creation and transport class.
22
+ * @package PHPMailer
23
+ * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
24
+ * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
25
+ * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
26
+ * @author Brent R. Matzelle (original founder)
27
+ */
28
+ class PHPMailer
29
+ {
30
+ /**
31
+ * The PHPMailer Version number.
32
+ * @type string
33
+ */
34
+ public $Version = '5.2.8';
35
+
36
+ /**
37
+ * Email priority.
38
+ * Options: 1 = High, 3 = Normal, 5 = low.
39
+ * @type integer
40
+ */
41
+ public $Priority = 3;
42
+
43
+ /**
44
+ * The character set of the message.
45
+ * @type string
46
+ */
47
+ public $CharSet = 'iso-8859-1';
48
+
49
+ /**
50
+ * The MIME Content-type of the message.
51
+ * @type string
52
+ */
53
+ public $ContentType = 'text/plain';
54
+
55
+ /**
56
+ * The message encoding.
57
+ * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
58
+ * @type string
59
+ */
60
+ public $Encoding = '8bit';
61
+
62
+ /**
63
+ * Holds the most recent mailer error message.
64
+ * @type string
65
+ */
66
+ public $ErrorInfo = '';
67
+
68
+ /**
69
+ * The From email address for the message.
70
+ * @type string
71
+ */
72
+ public $From = 'root@localhost';
73
+
74
+ /**
75
+ * The From name of the message.
76
+ * @type string
77
+ */
78
+ public $FromName = 'Root User';
79
+
80
+ /**
81
+ * The Sender email (Return-Path) of the message.
82
+ * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
83
+ * @type string
84
+ */
85
+ public $Sender = '';
86
+
87
+ /**
88
+ * The Return-Path of the message.
89
+ * If empty, it will be set to either From or Sender.
90
+ * @type string
91
+ * @deprecated Email senders should never set a return-path header;
92
+ * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
93
+ * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
94
+ */
95
+ public $ReturnPath = '';
96
+
97
+ /**
98
+ * The Subject of the message.
99
+ * @type string
100
+ */
101
+ public $Subject = '';
102
+
103
+ /**
104
+ * An HTML or plain text message body.
105
+ * If HTML then call isHTML(true).
106
+ * @type string
107
+ */
108
+ public $Body = '';
109
+
110
+ /**
111
+ * The plain-text message body.
112
+ * This body can be read by mail clients that do not have HTML email
113
+ * capability such as mutt & Eudora.
114
+ * Clients that can read HTML will view the normal Body.
115
+ * @type string
116
+ */
117
+ public $AltBody = '';
118
+
119
+ /**
120
+ * An iCal message part body.
121
+ * Only supported in simple alt or alt_inline message types
122
+ * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
123
+ * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
124
+ * @link http://kigkonsult.se/iCalcreator/
125
+ * @type string
126
+ */
127
+ public $Ical = '';
128
+
129
+ /**
130
+ * The complete compiled MIME message body.
131
+ * @access protected
132
+ * @type string
133
+ */
134
+ protected $MIMEBody = '';
135
+
136
+ /**
137
+ * The complete compiled MIME message headers.
138
+ * @type string
139
+ * @access protected
140
+ */
141
+ protected $MIMEHeader = '';
142
+
143
+ /**
144
+ * Extra headers that createHeader() doesn't fold in.
145
+ * @type string
146
+ * @access protected
147
+ */
148
+ protected $mailHeader = '';
149
+
150
+ /**
151
+ * Word-wrap the message body to this number of chars.
152
+ * @type integer
153
+ */
154
+ public $WordWrap = 0;
155
+
156
+ /**
157
+ * Which method to use to send mail.
158
+ * Options: "mail", "sendmail", or "smtp".
159
+ * @type string
160
+ */
161
+ public $Mailer = 'mail';
162
+
163
+ /**
164
+ * The path to the sendmail program.
165
+ * @type string
166
+ */
167
+ public $Sendmail = '/usr/sbin/sendmail';
168
+
169
+ /**
170
+ * Whether mail() uses a fully sendmail-compatible MTA.
171
+ * One which supports sendmail's "-oi -f" options.
172
+ * @type boolean
173
+ */
174
+ public $UseSendmailOptions = true;
175
+
176
+ /**
177
+ * Path to PHPMailer plugins.
178
+ * Useful if the SMTP class is not in the PHP include path.
179
+ * @type string
180
+ * @deprecated Should not be needed now there is an autoloader.
181
+ */
182
+ public $PluginDir = '';
183
+
184
+ /**
185
+ * The email address that a reading confirmation should be sent to.
186
+ * @type string
187
+ */
188
+ public $ConfirmReadingTo = '';
189
+
190
+ /**
191
+ * The hostname to use in Message-Id and Received headers
192
+ * and as default HELO string.
193
+ * If empty, the value returned
194
+ * by SERVER_NAME is used or 'localhost.localdomain'.
195
+ * @type string
196
+ */
197
+ public $Hostname = '';
198
+
199
+ /**
200
+ * An ID to be used in the Message-Id header.
201
+ * If empty, a unique id will be generated.
202
+ * @type string
203
+ */
204
+ public $MessageID = '';
205
+
206
+ /**
207
+ * The message Date to be used in the Date header.
208
+ * If empty, the current date will be added.
209
+ * @type string
210
+ */
211
+ public $MessageDate = '';
212
+
213
+ /**
214
+ * SMTP hosts.
215
+ * Either a single hostname or multiple semicolon-delimited hostnames.
216
+ * You can also specify a different port
217
+ * for each host by using this format: [hostname:port]
218
+ * (e.g. "smtp1.example.com:25;smtp2.example.com").
219
+ * Hosts will be tried in order.
220
+ * @type string
221
+ */
222
+ public $Host = 'localhost';
223
+
224
+ /**
225
+ * The default SMTP server port.
226
+ * @type integer
227
+ * @TODO Why is this needed when the SMTP class takes care of it?
228
+ */
229
+ public $Port = 25;
230
+
231
+ /**
232
+ * The SMTP HELO of the message.
233
+ * Default is $Hostname.
234
+ * @type string
235
+ * @see PHPMailer::$Hostname
236
+ */
237
+ public $Helo = '';
238
+
239
+ /**
240
+ * The secure connection prefix.
241
+ * Options: "", "ssl" or "tls"
242
+ * @type string
243
+ */
244
+ public $SMTPSecure = '';
245
+
246
+ /**
247
+ * Whether to use SMTP authentication.
248
+ * Uses the Username and Password properties.
249
+ * @type boolean
250
+ * @see PHPMailer::$Username
251
+ * @see PHPMailer::$Password
252
+ */
253
+ public $SMTPAuth = false;
254
+
255
+ /**
256
+ * SMTP username.
257
+ * @type string
258
+ */
259
+ public $Username = '';
260
+
261
+ /**
262
+ * SMTP password.
263
+ * @type string
264
+ */
265
+ public $Password = '';
266
+
267
+ /**
268
+ * SMTP auth type.
269
+ * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
270
+ * @type string
271
+ */
272
+ public $AuthType = '';
273
+
274
+ /**
275
+ * SMTP realm.
276
+ * Used for NTLM auth
277
+ * @type string
278
+ */
279
+ public $Realm = '';
280
+
281
+ /**
282
+ * SMTP workstation.
283
+ * Used for NTLM auth
284
+ * @type string
285
+ */
286
+ public $Workstation = '';
287
+
288
+ /**
289
+ * The SMTP server timeout in seconds.
290
+ * @type integer
291
+ */
292
+ public $Timeout = 10;
293
+
294
+ /**
295
+ * SMTP class debug output mode.
296
+ * Options:
297
+ * 0: no output
298
+ * 1: commands
299
+ * 2: data and commands
300
+ * 3: as 2 plus connection status
301
+ * 4: low level data output
302
+ * @type integer
303
+ * @see SMTP::$do_debug
304
+ */
305
+ public $SMTPDebug = 0;
306
+
307
+ /**
308
+ * How to handle debug output.
309
+ * Options:
310
+ * 'echo': Output plain-text as-is, appropriate for CLI
311
+ * 'html': Output escaped, line breaks converted to <br>, appropriate for browser output
312
+ * 'error_log': Output to error log as configured in php.ini
313
+ * @type string
314
+ * @see SMTP::$Debugoutput
315
+ */
316
+ public $Debugoutput = 'echo';
317
+
318
+ /**
319
+ * Whether to keep SMTP connection open after each message.
320
+ * If this is set to true then to close the connection
321
+ * requires an explicit call to smtpClose().
322
+ * @type boolean
323
+ */
324
+ public $SMTPKeepAlive = false;
325
+
326
+ /**
327
+ * Whether to split multiple to addresses into multiple messages
328
+ * or send them all in one message.
329
+ * @type boolean
330
+ */
331
+ public $SingleTo = false;
332
+
333
+ /**
334
+ * Storage for addresses when SingleTo is enabled.
335
+ * @type array
336
+ * @TODO This should really not be public
337
+ */
338
+ public $SingleToArray = array();
339
+
340
+ /**
341
+ * Whether to generate VERP addresses on send.
342
+ * Only applicable when sending via SMTP.
343
+ * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
344
+ * @link http://www.postfix.org/VERP_README.html Postfix VERP info
345
+ * @type boolean
346
+ */
347
+ public $do_verp = false;
348
+
349
+ /**
350
+ * Whether to allow sending messages with an empty body.
351
+ * @type boolean
352
+ */
353
+ public $AllowEmpty = false;
354
+
355
+ /**
356
+ * The default line ending.
357
+ * @note The default remains "\n". We force CRLF where we know
358
+ * it must be used via self::CRLF.
359
+ * @type string
360
+ */
361
+ public $LE = "\n";
362
+
363
+ /**
364
+ * DKIM selector.
365
+ * @type string
366
+ */
367
+ public $DKIM_selector = '';
368
+
369
+ /**
370
+ * DKIM Identity.
371
+ * Usually the email address used as the source of the email
372
+ * @type string
373
+ */
374
+ public $DKIM_identity = '';
375
+
376
+ /**
377
+ * DKIM passphrase.
378
+ * Used if your key is encrypted.
379
+ * @type string
380
+ */
381
+ public $DKIM_passphrase = '';
382
+
383
+ /**
384
+ * DKIM signing domain name.
385
+ * @example 'example.com'
386
+ * @type string
387
+ */
388
+ public $DKIM_domain = '';
389
+
390
+ /**
391
+ * DKIM private key file path.
392
+ * @type string
393
+ */
394
+ public $DKIM_private = '';
395
+
396
+ /**
397
+ * Callback Action function name.
398
+ *
399
+ * The function that handles the result of the send email action.
400
+ * It is called out by send() for each email sent.
401
+ *
402
+ * Value can be any php callable: http://www.php.net/is_callable
403
+ *
404
+ * Parameters:
405
+ * boolean $result result of the send action
406
+ * string $to email address of the recipient
407
+ * string $cc cc email addresses
408
+ * string $bcc bcc email addresses
409
+ * string $subject the subject
410
+ * string $body the email body
411
+ * string $from email address of sender
412
+ * @type string
413
+ */
414
+ public $action_function = '';
415
+
416
+ /**
417
+ * What to use in the X-Mailer header.
418
+ * Options: null for default, whitespace for none, or a string to use
419
+ * @type string
420
+ */
421
+ public $XMailer = '';
422
+
423
+ /**
424
+ * An instance of the SMTP sender class.
425
+ * @type SMTP
426
+ * @access protected
427
+ */
428
+ protected $smtp = null;
429
+
430
+ /**
431
+ * The array of 'to' addresses.
432
+ * @type array
433
+ * @access protected
434
+ */
435
+ protected $to = array();
436
+
437
+ /**
438
+ * The array of 'cc' addresses.
439
+ * @type array
440
+ * @access protected
441
+ */
442
+ protected $cc = array();
443
+
444
+ /**
445
+ * The array of 'bcc' addresses.
446
+ * @type array
447
+ * @access protected
448
+ */
449
+ protected $bcc = array();
450
+
451
+ /**
452
+ * The array of reply-to names and addresses.
453
+ * @type array
454
+ * @access protected
455
+ */
456
+ protected $ReplyTo = array();
457
+
458
+ /**
459
+ * An array of all kinds of addresses.
460
+ * Includes all of $to, $cc, $bcc, $replyto
461
+ * @type array
462
+ * @access protected
463
+ */
464
+ protected $all_recipients = array();
465
+
466
+ /**
467
+ * The array of attachments.
468
+ * @type array
469
+ * @access protected
470
+ */
471
+ protected $attachment = array();
472
+
473
+ /**
474
+ * The array of custom headers.
475
+ * @type array
476
+ * @access protected
477
+ */
478
+ protected $CustomHeader = array();
479
+
480
+ /**
481
+ * The most recent Message-ID (including angular brackets).
482
+ * @type string
483
+ * @access protected
484
+ */
485
+ protected $lastMessageID = '';
486
+
487
+ /**
488
+ * The message's MIME type.
489
+ * @type string
490
+ * @access protected
491
+ */
492
+ protected $message_type = '';
493
+
494
+ /**
495
+ * The array of MIME boundary strings.
496
+ * @type array
497
+ * @access protected
498
+ */
499
+ protected $boundary = array();
500
+
501
+ /**
502
+ * The array of available languages.
503
+ * @type array
504
+ * @access protected
505
+ */
506
+ protected $language = array();
507
+
508
+ /**
509
+ * The number of errors encountered.
510
+ * @type integer
511
+ * @access protected
512
+ */
513
+ protected $error_count = 0;
514
+
515
+ /**
516
+ * The S/MIME certificate file path.
517
+ * @type string
518
+ * @access protected
519
+ */
520
+ protected $sign_cert_file = '';
521
+
522
+ /**
523
+ * The S/MIME key file path.
524
+ * @type string
525
+ * @access protected
526
+ */
527
+ protected $sign_key_file = '';
528
+
529
+ /**
530
+ * The S/MIME password for the key.
531
+ * Used only if the key is encrypted.
532
+ * @type string
533
+ * @access protected
534
+ */
535
+ protected $sign_key_pass = '';
536
+
537
+ /**
538
+ * Whether to throw exceptions for errors.
539
+ * @type boolean
540
+ * @access protected
541
+ */
542
+ protected $exceptions = false;
543
+
544
+ /**
545
+ * Error severity: message only, continue processing
546
+ */
547
+ const STOP_MESSAGE = 0;
548
+
549
+ /**
550
+ * Error severity: message, likely ok to continue processing
551
+ */
552
+ const STOP_CONTINUE = 1;
553
+
554
+ /**
555
+ * Error severity: message, plus full stop, critical error reached
556
+ */
557
+ const STOP_CRITICAL = 2;
558
+
559
+ /**
560
+ * SMTP RFC standard line ending
561
+ */
562
+ const CRLF = "\r\n";
563
+
564
+ /**
565
+ * Constructor
566
+ * @param boolean $exceptions Should we throw external exceptions?
567
+ */
568
+ public function __construct($exceptions = false)
569
+ {
570
+ $this->exceptions = ($exceptions == true);
571
+ //Make sure our autoloader is loaded
572
+ if (version_compare(PHP_VERSION, '5.1.2', '>=')) {
573
+ $autoload = spl_autoload_functions();
574
+ if ($autoload === false or !in_array('PHPMailerAutoload', $autoload)) {
575
+ require 'PHPMailerAutoload.php';
576
+ }
577
+ }
578
+ }
579
+
580
+ /**
581
+ * Destructor.
582
+ */
583
+ public function __destruct()
584
+ {
585
+ if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
586
+ $this->smtpClose();
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Call mail() in a safe_mode-aware fashion.
592
+ * Also, unless sendmail_path points to sendmail (or something that
593
+ * claims to be sendmail), don't pass params (not a perfect fix,
594
+ * but it will do)
595
+ * @param string $to To
596
+ * @param string $subject Subject
597
+ * @param string $body Message Body
598
+ * @param string $header Additional Header(s)
599
+ * @param string $params Params
600
+ * @access private
601
+ * @return boolean
602
+ */
603
+ private function mailPassthru($to, $subject, $body, $header, $params)
604
+ {
605
+ //Check overloading of mail function to avoid double-encoding
606
+ if (ini_get('mbstring.func_overload') & 1) {
607
+ $subject = $this->secureHeader($subject);
608
+ } else {
609
+ $subject = $this->encodeHeader($this->secureHeader($subject));
610
+ }
611
+ if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
612
+ $result = @mail($to, $subject, $body, $header);
613
+ } else {
614
+ $result = @mail($to, $subject, $body, $header, $params);
615
+ }
616
+ return $result;
617
+ }
618
+
619
+ /**
620
+ * Output debugging info via user-defined method.
621
+ * Only if debug output is enabled.
622
+ * @see PHPMailer::$Debugoutput
623
+ * @see PHPMailer::$SMTPDebug
624
+ * @param string $str
625
+ */
626
+ protected function edebug($str)
627
+ {
628
+ if (!$this->SMTPDebug) {
629
+ return;
630
+ }
631
+ switch ($this->Debugoutput) {
632
+ case 'error_log':
633
+ error_log($str);
634
+ break;
635
+ case 'html':
636
+ //Cleans up output a bit for a better looking display that's HTML-safe
637
+ echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . "<br>\n";
638
+ break;
639
+ case 'echo':
640
+ default:
641
+ echo $str."\n";
642
+ }
643
+ }
644
+
645
+ /**
646
+ * Sets message type to HTML or plain.
647
+ * @param boolean $isHtml True for HTML mode.
648
+ * @return void
649
+ */
650
+ public function isHTML($isHtml = true)
651
+ {
652
+ if ($isHtml) {
653
+ $this->ContentType = 'text/html';
654
+ } else {
655
+ $this->ContentType = 'text/plain';
656
+ }
657
+ }
658
+
659
+ /**
660
+ * Send messages using SMTP.
661
+ * @return void
662
+ */
663
+ public function isSMTP()
664
+ {
665
+ $this->Mailer = 'smtp';
666
+ }
667
+
668
+ /**
669
+ * Send messages using PHP's mail() function.
670
+ * @return void
671
+ */
672
+ public function isMail()
673
+ {
674
+ $this->Mailer = 'mail';
675
+ }
676
+
677
+ /**
678
+ * Send messages using $Sendmail.
679
+ * @return void
680
+ */
681
+ public function isSendmail()
682
+ {
683
+ $ini_sendmail_path = ini_get('sendmail_path');
684
+
685
+ if (!stristr($ini_sendmail_path, 'sendmail')) {
686
+ $this->Sendmail = '/usr/sbin/sendmail';
687
+ } else {
688
+ $this->Sendmail = $ini_sendmail_path;
689
+ }
690
+ $this->Mailer = 'sendmail';
691
+ }
692
+
693
+ /**
694
+ * Send messages using qmail.
695
+ * @return void
696
+ */
697
+ public function isQmail()
698
+ {
699
+ $ini_sendmail_path = ini_get('sendmail_path');
700
+
701
+ if (!stristr($ini_sendmail_path, 'qmail')) {
702
+ $this->Sendmail = '/var/qmail/bin/qmail-inject';
703
+ } else {
704
+ $this->Sendmail = $ini_sendmail_path;
705
+ }
706
+ $this->Mailer = 'qmail';
707
+ }
708
+
709
+ /**
710
+ * Add a "To" address.
711
+ * @param string $address
712
+ * @param string $name
713
+ * @return boolean true on success, false if address already used
714
+ */
715
+ public function addAddress($address, $name = '')
716
+ {
717
+ return $this->addAnAddress('to', $address, $name);
718
+ }
719
+
720
+ /**
721
+ * Add a "CC" address.
722
+ * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
723
+ * @param string $address
724
+ * @param string $name
725
+ * @return boolean true on success, false if address already used
726
+ */
727
+ public function addCC($address, $name = '')
728
+ {
729
+ return $this->addAnAddress('cc', $address, $name);
730
+ }
731
+
732
+ /**
733
+ * Add a "BCC" address.
734
+ * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
735
+ * @param string $address
736
+ * @param string $name
737
+ * @return boolean true on success, false if address already used
738
+ */
739
+ public function addBCC($address, $name = '')
740
+ {
741
+ return $this->addAnAddress('bcc', $address, $name);
742
+ }
743
+
744
+ /**
745
+ * Add a "Reply-to" address.
746
+ * @param string $address
747
+ * @param string $name
748
+ * @return boolean
749
+ */
750
+ public function addReplyTo($address, $name = '')
751
+ {
752
+ return $this->addAnAddress('Reply-To', $address, $name);
753
+ }
754
+
755
+ /**
756
+ * Add an address to one of the recipient arrays.
757
+ * Addresses that have been added already return false, but do not throw exceptions
758
+ * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
759
+ * @param string $address The email address to send to
760
+ * @param string $name
761
+ * @throws phpmailerException
762
+ * @return boolean true on success, false if address already used or invalid in some way
763
+ * @access protected
764
+ */
765
+ protected function addAnAddress($kind, $address, $name = '')
766
+ {
767
+ if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
768
+ $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
769
+ $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
770
+ if ($this->exceptions) {
771
+ throw new phpmailerException('Invalid recipient array: ' . $kind);
772
+ }
773
+ return false;
774
+ }
775
+ $address = trim($address);
776
+ $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
777
+ if (!$this->validateAddress($address)) {
778
+ $this->setError($this->lang('invalid_address') . ': ' . $address);
779
+ $this->edebug($this->lang('invalid_address') . ': ' . $address);
780
+ if ($this->exceptions) {
781
+ throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
782
+ }
783
+ return false;
784
+ }
785
+ if ($kind != 'Reply-To') {
786
+ if (!isset($this->all_recipients[strtolower($address)])) {
787
+ array_push($this->$kind, array($address, $name));
788
+ $this->all_recipients[strtolower($address)] = true;
789
+ return true;
790
+ }
791
+ } else {
792
+ if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
793
+ $this->ReplyTo[strtolower($address)] = array($address, $name);
794
+ return true;
795
+ }
796
+ }
797
+ return false;
798
+ }
799
+
800
+ /**
801
+ * Set the From and FromName properties.
802
+ * @param string $address
803
+ * @param string $name
804
+ * @param boolean $auto Whether to also set the Sender address, defaults to true
805
+ * @throws phpmailerException
806
+ * @return boolean
807
+ */
808
+ public function setFrom($address, $name = '', $auto = true)
809
+ {
810
+ $address = trim($address);
811
+ $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
812
+ if (!$this->validateAddress($address)) {
813
+ $this->setError($this->lang('invalid_address') . ': ' . $address);
814
+ $this->edebug($this->lang('invalid_address') . ': ' . $address);
815
+ if ($this->exceptions) {
816
+ throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
817
+ }
818
+ return false;
819
+ }
820
+ $this->From = $address;
821
+ $this->FromName = $name;
822
+ if ($auto) {
823
+ if (empty($this->Sender)) {
824
+ $this->Sender = $address;
825
+ }
826
+ }
827
+ return true;
828
+ }
829
+
830
+ /**
831
+ * Return the Message-ID header of the last email.
832
+ * Technically this is the value from the last time the headers were created,
833
+ * but it's also the message ID of the last sent message except in
834
+ * pathological cases.
835
+ * @return string
836
+ */
837
+ public function getLastMessageID()
838
+ {
839
+ return $this->lastMessageID;
840
+ }
841
+
842
+ /**
843
+ * Check that a string looks like an email address.
844
+ * @param string $address The email address to check
845
+ * @param string $patternselect A selector for the validation pattern to use :
846
+ * * `auto` Pick strictest one automatically;
847
+ * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
848
+ * * `pcre` Use old PCRE implementation;
849
+ * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
850
+ * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
851
+ * * `noregex` Don't use a regex: super fast, really dumb.
852
+ * @return boolean
853
+ * @static
854
+ * @access public
855
+ */
856
+ public static function validateAddress($address, $patternselect = 'auto')
857
+ {
858
+ if (!$patternselect or $patternselect == 'auto') {
859
+ if (defined('PCRE_VERSION')) { //Check this constant so it works when extension_loaded() is disabled
860
+ if (version_compare(PCRE_VERSION, '8.0') >= 0) {
861
+ $patternselect = 'pcre8';
862
+ } else {
863
+ $patternselect = 'pcre';
864
+ }
865
+ } else {
866
+ //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
867
+ if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
868
+ $patternselect = 'php';
869
+ } else {
870
+ $patternselect = 'noregex';
871
+ }
872
+ }
873
+ }
874
+ switch ($patternselect) {
875
+ case 'pcre8':
876
+ /**
877
+ * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
878
+ * @link http://squiloople.com/2009/12/20/email-address-validation/
879
+ * @copyright 2009-2010 Michael Rushton
880
+ * Feel free to use and redistribute this code. But please keep this copyright notice.
881
+ */
882
+ return (boolean)preg_match(
883
+ '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
884
+ '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
885
+ '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
886
+ '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
887
+ '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
888
+ '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
889
+ '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
890
+ '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
891
+ '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
892
+ $address
893
+ );
894
+ case 'pcre':
895
+ //An older regex that doesn't need a recent PCRE
896
+ return (boolean)preg_match(
897
+ '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
898
+ '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
899
+ '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
900
+ '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
901
+ '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
902
+ '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
903
+ '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
904
+ '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
905
+ '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
906
+ '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
907
+ $address
908
+ );
909
+ case 'html5':
910
+ /**
911
+ * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
912
+ * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
913
+ */
914
+ return (boolean)preg_match(
915
+ '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
916
+ '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
917
+ $address
918
+ );
919
+ case 'noregex':
920
+ //No PCRE! Do something _very_ approximate!
921
+ //Check the address is 3 chars or longer and contains an @ that's not the first or last char
922
+ return (strlen($address) >= 3
923
+ and strpos($address, '@') >= 1
924
+ and strpos($address, '@') != strlen($address) - 1);
925
+ case 'php':
926
+ default:
927
+ return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
928
+ }
929
+ }
930
+
931
+ /**
932
+ * Create a message and send it.
933
+ * Uses the sending method specified by $Mailer.
934
+ * @throws phpmailerException
935
+ * @return boolean false on error - See the ErrorInfo property for details of the error.
936
+ */
937
+ public function send()
938
+ {
939
+ try {
940
+ if (!$this->preSend()) {
941
+ return false;
942
+ }
943
+ return $this->postSend();
944
+ } catch (phpmailerException $exc) {
945
+ $this->mailHeader = '';
946
+ $this->setError($exc->getMessage());
947
+ if ($this->exceptions) {
948
+ throw $exc;
949
+ }
950
+ return false;
951
+ }
952
+ }
953
+
954
+ /**
955
+ * Prepare a message for sending.
956
+ * @throws phpmailerException
957
+ * @return boolean
958
+ */
959
+ public function preSend()
960
+ {
961
+ try {
962
+ $this->mailHeader = '';
963
+ if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
964
+ throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
965
+ }
966
+
967
+ // Set whether the message is multipart/alternative
968
+ if (!empty($this->AltBody)) {
969
+ $this->ContentType = 'multipart/alternative';
970
+ }
971
+
972
+ $this->error_count = 0; // reset errors
973
+ $this->setMessageType();
974
+ // Refuse to send an empty message unless we are specifically allowing it
975
+ if (!$this->AllowEmpty and empty($this->Body)) {
976
+ throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
977
+ }
978
+
979
+ $this->MIMEHeader = $this->createHeader();
980
+ $this->MIMEBody = $this->createBody();
981
+
982
+ // To capture the complete message when using mail(), create
983
+ // an extra header list which createHeader() doesn't fold in
984
+ if ($this->Mailer == 'mail') {
985
+ if (count($this->to) > 0) {
986
+ $this->mailHeader .= $this->addrAppend('To', $this->to);
987
+ } else {
988
+ $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
989
+ }
990
+ $this->mailHeader .= $this->headerLine(
991
+ 'Subject',
992
+ $this->encodeHeader($this->secureHeader(trim($this->Subject)))
993
+ );
994
+ }
995
+
996
+ // Sign with DKIM if enabled
997
+ if (!empty($this->DKIM_domain)
998
+ && !empty($this->DKIM_private)
999
+ && !empty($this->DKIM_selector)
1000
+ && !empty($this->DKIM_domain)
1001
+ && file_exists($this->DKIM_private)) {
1002
+ $header_dkim = $this->DKIM_Add(
1003
+ $this->MIMEHeader . $this->mailHeader,
1004
+ $this->encodeHeader($this->secureHeader($this->Subject)),
1005
+ $this->MIMEBody
1006
+ );
1007
+ $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1008
+ str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1009
+ }
1010
+ return true;
1011
+
1012
+ } catch (phpmailerException $exc) {
1013
+ $this->setError($exc->getMessage());
1014
+ if ($this->exceptions) {
1015
+ throw $exc;
1016
+ }
1017
+ return false;
1018
+ }
1019
+ }
1020
+
1021
+ /**
1022
+ * Actually send a message.
1023
+ * Send the email via the selected mechanism
1024
+ * @throws phpmailerException
1025
+ * @return boolean
1026
+ */
1027
+ public function postSend()
1028
+ {
1029
+ try {
1030
+ // Choose the mailer and send through it
1031
+ switch ($this->Mailer) {
1032
+ case 'sendmail':
1033
+ case 'qmail':
1034
+ return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1035
+ case 'smtp':
1036
+ return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1037
+ case 'mail':
1038
+ return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1039
+ default:
1040
+ $sendMethod = $this->Mailer.'Send';
1041
+ if (method_exists($this, $sendMethod)) {
1042
+ return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1043
+ }
1044
+
1045
+ return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1046
+ }
1047
+ } catch (phpmailerException $exc) {
1048
+ $this->setError($exc->getMessage());
1049
+ $this->edebug($exc->getMessage());
1050
+ if ($this->exceptions) {
1051
+ throw $exc;
1052
+ }
1053
+ }
1054
+ return false;
1055
+ }
1056
+
1057
+ /**
1058
+ * Send mail using the $Sendmail program.
1059
+ * @param string $header The message headers
1060
+ * @param string $body The message body
1061
+ * @see PHPMailer::$Sendmail
1062
+ * @throws phpmailerException
1063
+ * @access protected
1064
+ * @return boolean
1065
+ */
1066
+ protected function sendmailSend($header, $body)
1067
+ {
1068
+ if ($this->Sender != '') {
1069
+ if ($this->Mailer == 'qmail') {
1070
+ $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1071
+ } else {
1072
+ $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1073
+ }
1074
+ } else {
1075
+ if ($this->Mailer == 'qmail') {
1076
+ $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1077
+ } else {
1078
+ $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1079
+ }
1080
+ }
1081
+ if ($this->SingleTo === true) {
1082
+ foreach ($this->SingleToArray as $toAddr) {
1083
+ if (!@$mail = popen($sendmail, 'w')) {
1084
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1085
+ }
1086
+ fputs($mail, 'To: ' . $toAddr . "\n");
1087
+ fputs($mail, $header);
1088
+ fputs($mail, $body);
1089
+ $result = pclose($mail);
1090
+ $this->doCallback(
1091
+ ($result == 0),
1092
+ array($toAddr),
1093
+ $this->cc,
1094
+ $this->bcc,
1095
+ $this->Subject,
1096
+ $body,
1097
+ $this->From
1098
+ );
1099
+ if ($result != 0) {
1100
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1101
+ }
1102
+ }
1103
+ } else {
1104
+ if (!@$mail = popen($sendmail, 'w')) {
1105
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1106
+ }
1107
+ fputs($mail, $header);
1108
+ fputs($mail, $body);
1109
+ $result = pclose($mail);
1110
+ $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1111
+ if ($result != 0) {
1112
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1113
+ }
1114
+ }
1115
+ return true;
1116
+ }
1117
+
1118
+ /**
1119
+ * Send mail using the PHP mail() function.
1120
+ * @param string $header The message headers
1121
+ * @param string $body The message body
1122
+ * @link http://www.php.net/manual/en/book.mail.php
1123
+ * @throws phpmailerException
1124
+ * @access protected
1125
+ * @return boolean
1126
+ */
1127
+ protected function mailSend($header, $body)
1128
+ {
1129
+ $toArr = array();
1130
+ foreach ($this->to as $toaddr) {
1131
+ $toArr[] = $this->addrFormat($toaddr);
1132
+ }
1133
+ $to = implode(', ', $toArr);
1134
+
1135
+ if (empty($this->Sender)) {
1136
+ $params = ' ';
1137
+ } else {
1138
+ $params = sprintf('-f%s', $this->Sender);
1139
+ }
1140
+ if ($this->Sender != '' and !ini_get('safe_mode')) {
1141
+ $old_from = ini_get('sendmail_from');
1142
+ ini_set('sendmail_from', $this->Sender);
1143
+ }
1144
+ $result = false;
1145
+ if ($this->SingleTo === true && count($toArr) > 1) {
1146
+ foreach ($toArr as $toAddr) {
1147
+ $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1148
+ $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1149
+ }
1150
+ } else {
1151
+ $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1152
+ $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1153
+ }
1154
+ if (isset($old_from)) {
1155
+ ini_set('sendmail_from', $old_from);
1156
+ }
1157
+ if (!$result) {
1158
+ throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1159
+ }
1160
+ return true;
1161
+ }
1162
+
1163
+ /**
1164
+ * Get an instance to use for SMTP operations.
1165
+ * Override this function to load your own SMTP implementation
1166
+ * @return SMTP
1167
+ */
1168
+ public function getSMTPInstance()
1169
+ {
1170
+ if (!is_object($this->smtp)) {
1171
+ $this->smtp = new SMTP;
1172
+ }
1173
+ return $this->smtp;
1174
+ }
1175
+
1176
+ /**
1177
+ * Send mail via SMTP.
1178
+ * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1179
+ * Uses the PHPMailerSMTP class by default.
1180
+ * @see PHPMailer::getSMTPInstance() to use a different class.
1181
+ * @param string $header The message headers
1182
+ * @param string $body The message body
1183
+ * @throws phpmailerException
1184
+ * @uses SMTP
1185
+ * @access protected
1186
+ * @return boolean
1187
+ */
1188
+ protected function smtpSend($header, $body)
1189
+ {
1190
+ $bad_rcpt = array();
1191
+
1192
+ if (!$this->smtpConnect()) {
1193
+ throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1194
+ }
1195
+ $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
1196
+ if (!$this->smtp->mail($smtp_from)) {
1197
+ $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1198
+ throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1199
+ }
1200
+
1201
+ // Attempt to send to all recipients
1202
+ foreach ($this->to as $to) {
1203
+ if (!$this->smtp->recipient($to[0])) {
1204
+ $bad_rcpt[] = $to[0];
1205
+ $isSent = false;
1206
+ } else {
1207
+ $isSent = true;
1208
+ }
1209
+ $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1210
+ }
1211
+ foreach ($this->cc as $cc) {
1212
+ if (!$this->smtp->recipient($cc[0])) {
1213
+ $bad_rcpt[] = $cc[0];
1214
+ $isSent = false;
1215
+ } else {
1216
+ $isSent = true;
1217
+ }
1218
+ $this->doCallback($isSent, array(), array($cc[0]), array(), $this->Subject, $body, $this->From);
1219
+ }
1220
+ foreach ($this->bcc as $bcc) {
1221
+ if (!$this->smtp->recipient($bcc[0])) {
1222
+ $bad_rcpt[] = $bcc[0];
1223
+ $isSent = false;
1224
+ } else {
1225
+ $isSent = true;
1226
+ }
1227
+ $this->doCallback($isSent, array(), array(), array($bcc[0]), $this->Subject, $body, $this->From);
1228
+ }
1229
+
1230
+ // Only send the DATA command if we have viable recipients
1231
+ if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1232
+ throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1233
+ }
1234
+ if ($this->SMTPKeepAlive == true) {
1235
+ $this->smtp->reset();
1236
+ } else {
1237
+ $this->smtp->quit();
1238
+ $this->smtp->close();
1239
+ }
1240
+ if (count($bad_rcpt) > 0) { // Create error message for any bad addresses
1241
+ throw new phpmailerException(
1242
+ $this->lang('recipients_failed') . implode(', ', $bad_rcpt),
1243
+ self::STOP_CONTINUE
1244
+ );
1245
+ }
1246
+ return true;
1247
+ }
1248
+
1249
+ /**
1250
+ * Initiate a connection to an SMTP server.
1251
+ * Returns false if the operation failed.
1252
+ * @param array $options An array of options compatible with stream_context_create()
1253
+ * @uses SMTP
1254
+ * @access public
1255
+ * @throws phpmailerException
1256
+ * @return boolean
1257
+ */
1258
+ public function smtpConnect($options = array())
1259
+ {
1260
+ if (is_null($this->smtp)) {
1261
+ $this->smtp = $this->getSMTPInstance();
1262
+ }
1263
+
1264
+ // Already connected?
1265
+ if ($this->smtp->connected()) {
1266
+ return true;
1267
+ }
1268
+
1269
+ $this->smtp->setTimeout($this->Timeout);
1270
+ $this->smtp->setDebugLevel($this->SMTPDebug);
1271
+ $this->smtp->setDebugOutput($this->Debugoutput);
1272
+ $this->smtp->setVerp($this->do_verp);
1273
+ $hosts = explode(';', $this->Host);
1274
+ $lastexception = null;
1275
+
1276
+ foreach ($hosts as $hostentry) {
1277
+ $hostinfo = array();
1278
+ if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1279
+ // Not a valid host entry
1280
+ continue;
1281
+ }
1282
+ // $hostinfo[2]: optional ssl or tls prefix
1283
+ // $hostinfo[3]: the hostname
1284
+ // $hostinfo[4]: optional port number
1285
+ // The host string prefix can temporarily override the current setting for SMTPSecure
1286
+ // If it's not specified, the default value is used
1287
+ $prefix = '';
1288
+ $tls = ($this->SMTPSecure == 'tls');
1289
+ if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) {
1290
+ $prefix = 'ssl://';
1291
+ $tls = false; // Can't have SSL and TLS at once
1292
+ } elseif ($hostinfo[2] == 'tls') {
1293
+ $tls = true;
1294
+ // tls doesn't use a prefix
1295
+ }
1296
+ $host = $hostinfo[3];
1297
+ $port = $this->Port;
1298
+ $tport = (integer)$hostinfo[4];
1299
+ if ($tport > 0 and $tport < 65536) {
1300
+ $port = $tport;
1301
+ }
1302
+ if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1303
+ try {
1304
+ if ($this->Helo) {
1305
+ $hello = $this->Helo;
1306
+ } else {
1307
+ $hello = $this->serverHostname();
1308
+ }
1309
+ $this->smtp->hello($hello);
1310
+
1311
+ if ($tls) {
1312
+ if (!$this->smtp->startTLS()) {
1313
+ throw new phpmailerException($this->lang('connect_host'));
1314
+ }
1315
+ // We must resend HELO after tls negotiation
1316
+ $this->smtp->hello($hello);
1317
+ }
1318
+ if ($this->SMTPAuth) {
1319
+ if (!$this->smtp->authenticate(
1320
+ $this->Username,
1321
+ $this->Password,
1322
+ $this->AuthType,
1323
+ $this->Realm,
1324
+ $this->Workstation
1325
+ )
1326
+ ) {
1327
+ throw new phpmailerException($this->lang('authenticate'));
1328
+ }
1329
+ }
1330
+ return true;
1331
+ } catch (phpmailerException $exc) {
1332
+ $lastexception = $exc;
1333
+ // We must have connected, but then failed TLS or Auth, so close connection nicely
1334
+ $this->smtp->quit();
1335
+ }
1336
+ }
1337
+ }
1338
+ // If we get here, all connection attempts have failed, so close connection hard
1339
+ $this->smtp->close();
1340
+ // As we've caught all exceptions, just report whatever the last one was
1341
+ if ($this->exceptions and !is_null($lastexception)) {
1342
+ throw $lastexception;
1343
+ }
1344
+ return false;
1345
+ }
1346
+
1347
+ /**
1348
+ * Close the active SMTP session if one exists.
1349
+ * @return void
1350
+ */
1351
+ public function smtpClose()
1352
+ {
1353
+ if ($this->smtp !== null) {
1354
+ if ($this->smtp->connected()) {
1355
+ $this->smtp->quit();
1356
+ $this->smtp->close();
1357
+ }
1358
+ }
1359
+ }
1360
+
1361
+ /**
1362
+ * Set the language for error messages.
1363
+ * Returns false if it cannot load the language file.
1364
+ * The default language is English.
1365
+ * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1366
+ * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1367
+ * @return boolean
1368
+ * @access public
1369
+ */
1370
+ public function setLanguage($langcode = 'en', $lang_path = '')
1371
+ {
1372
+ // Define full set of translatable strings in English
1373
+ $PHPMAILER_LANG = array(
1374
+ 'authenticate' => 'SMTP Error: Could not authenticate.',
1375
+ 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1376
+ 'data_not_accepted' => 'SMTP Error: data not accepted.',
1377
+ 'empty_message' => 'Message body empty',
1378
+ 'encoding' => 'Unknown encoding: ',
1379
+ 'execute' => 'Could not execute: ',
1380
+ 'file_access' => 'Could not access file: ',
1381
+ 'file_open' => 'File Error: Could not open file: ',
1382
+ 'from_failed' => 'The following From address failed: ',
1383
+ 'instantiate' => 'Could not instantiate mail function.',
1384
+ 'invalid_address' => 'Invalid address',
1385
+ 'mailer_not_supported' => ' mailer is not supported.',
1386
+ 'provide_address' => 'You must provide at least one recipient email address.',
1387
+ 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1388
+ 'signing' => 'Signing Error: ',
1389
+ 'smtp_connect_failed' => 'SMTP connect() failed.',
1390
+ 'smtp_error' => 'SMTP server error: ',
1391
+ 'variable_set' => 'Cannot set or reset variable: '
1392
+ );
1393
+ if (empty($lang_path)) {
1394
+ // Calculate an absolute path so it can work if CWD is not here
1395
+ $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1396
+ }
1397
+ $foundlang = true;
1398
+ $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1399
+ if ($langcode != 'en') { // There is no English translation file
1400
+ // Make sure language file path is readable
1401
+ if (!is_readable($lang_file)) {
1402
+ $foundlang = false;
1403
+ } else {
1404
+ // Overwrite language-specific strings.
1405
+ // This way we'll never have missing translations.
1406
+ $foundlang = include $lang_file;
1407
+ }
1408
+ }
1409
+ $this->language = $PHPMAILER_LANG;
1410
+ return ($foundlang == true); // Returns false if language not found
1411
+ }
1412
+
1413
+ /**
1414
+ * Get the array of strings for the current language.
1415
+ * @return array
1416
+ */
1417
+ public function getTranslations()
1418
+ {
1419
+ return $this->language;
1420
+ }
1421
+
1422
+ /**
1423
+ * Create recipient headers.
1424
+ * @access public
1425
+ * @param string $type
1426
+ * @param array $addr An array of recipient,
1427
+ * where each recipient is a 2-element indexed array with element 0 containing an address
1428
+ * and element 1 containing a name, like:
1429
+ * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
1430
+ * @return string
1431
+ */
1432
+ public function addrAppend($type, $addr)
1433
+ {
1434
+ $addresses = array();
1435
+ foreach ($addr as $address) {
1436
+ $addresses[] = $this->addrFormat($address);
1437
+ }
1438
+ return $type . ': ' . implode(', ', $addresses) . $this->LE;
1439
+ }
1440
+
1441
+ /**
1442
+ * Format an address for use in a message header.
1443
+ * @access public
1444
+ * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1445
+ * like array('joe@example.com', 'Joe User')
1446
+ * @return string
1447
+ */
1448
+ public function addrFormat($addr)
1449
+ {
1450
+ if (empty($addr[1])) { // No name provided
1451
+ return $this->secureHeader($addr[0]);
1452
+ } else {
1453
+ return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1454
+ $addr[0]
1455
+ ) . '>';
1456
+ }
1457
+ }
1458
+
1459
+ /**
1460
+ * Word-wrap message.
1461
+ * For use with mailers that do not automatically perform wrapping
1462
+ * and for quoted-printable encoded messages.
1463
+ * Original written by philippe.
1464
+ * @param string $message The message to wrap
1465
+ * @param integer $length The line length to wrap to
1466
+ * @param boolean $qp_mode Whether to run in Quoted-Printable mode
1467
+ * @access public
1468
+ * @return string
1469
+ */
1470
+ public function wrapText($message, $length, $qp_mode = false)
1471
+ {
1472
+ $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;
1473
+ // If utf-8 encoding is used, we will need to make sure we don't
1474
+ // split multibyte characters when we wrap
1475
+ $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1476
+ $lelen = strlen($this->LE);
1477
+ $crlflen = strlen(self::CRLF);
1478
+
1479
+ $message = $this->fixEOL($message);
1480
+ if (substr($message, -$lelen) == $this->LE) {
1481
+ $message = substr($message, 0, -$lelen);
1482
+ }
1483
+
1484
+ $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
1485
+ $message = '';
1486
+ for ($i = 0; $i < count($line); $i++) {
1487
+ $line_part = explode(' ', $line[$i]);
1488
+ $buf = '';
1489
+ for ($e = 0; $e < count($line_part); $e++) {
1490
+ $word = $line_part[$e];
1491
+ if ($qp_mode and (strlen($word) > $length)) {
1492
+ $space_left = $length - strlen($buf) - $crlflen;
1493
+ if ($e != 0) {
1494
+ if ($space_left > 20) {
1495
+ $len = $space_left;
1496
+ if ($is_utf8) {
1497
+ $len = $this->utf8CharBoundary($word, $len);
1498
+ } elseif (substr($word, $len - 1, 1) == '=') {
1499
+ $len--;
1500
+ } elseif (substr($word, $len - 2, 1) == '=') {
1501
+ $len -= 2;
1502
+ }
1503
+ $part = substr($word, 0, $len);
1504
+ $word = substr($word, $len);
1505
+ $buf .= ' ' . $part;
1506
+ $message .= $buf . sprintf('=%s', self::CRLF);
1507
+ } else {
1508
+ $message .= $buf . $soft_break;
1509
+ }
1510
+ $buf = '';
1511
+ }
1512
+ while (strlen($word) > 0) {
1513
+ if ($length <= 0) {
1514
+ break;
1515
+ }
1516
+ $len = $length;
1517
+ if ($is_utf8) {
1518
+ $len = $this->utf8CharBoundary($word, $len);
1519
+ } elseif (substr($word, $len - 1, 1) == '=') {
1520
+ $len--;
1521
+ } elseif (substr($word, $len - 2, 1) == '=') {
1522
+ $len -= 2;
1523
+ }
1524
+ $part = substr($word, 0, $len);
1525
+ $word = substr($word, $len);
1526
+
1527
+ if (strlen($word) > 0) {
1528
+ $message .= $part . sprintf('=%s', self::CRLF);
1529
+ } else {
1530
+ $buf = $part;
1531
+ }
1532
+ }
1533
+ } else {
1534
+ $buf_o = $buf;
1535
+ $buf .= ($e == 0) ? $word : (' ' . $word);
1536
+
1537
+ if (strlen($buf) > $length and $buf_o != '') {
1538
+ $message .= $buf_o . $soft_break;
1539
+ $buf = $word;
1540
+ }
1541
+ }
1542
+ }
1543
+ $message .= $buf . self::CRLF;
1544
+ }
1545
+
1546
+ return $message;
1547
+ }
1548
+
1549
+ /**
1550
+ * Find the last character boundary prior to $maxLength in a utf-8
1551
+ * quoted (printable) encoded string.
1552
+ * Original written by Colin Brown.
1553
+ * @access public
1554
+ * @param string $encodedText utf-8 QP text
1555
+ * @param integer $maxLength find last character boundary prior to this length
1556
+ * @return integer
1557
+ */
1558
+ public function utf8CharBoundary($encodedText, $maxLength)
1559
+ {
1560
+ $foundSplitPos = false;
1561
+ $lookBack = 3;
1562
+ while (!$foundSplitPos) {
1563
+ $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1564
+ $encodedCharPos = strpos($lastChunk, '=');
1565
+ if ($encodedCharPos !== false) {
1566
+ // Found start of encoded character byte within $lookBack block.
1567
+ // Check the encoded byte value (the 2 chars after the '=')
1568
+ $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1569
+ $dec = hexdec($hex);
1570
+ if ($dec < 128) { // Single byte character.
1571
+ // If the encoded char was found at pos 0, it will fit
1572
+ // otherwise reduce maxLength to start of the encoded char
1573
+ $maxLength = ($encodedCharPos == 0) ? $maxLength :
1574
+ $maxLength - ($lookBack - $encodedCharPos);
1575
+ $foundSplitPos = true;
1576
+ } elseif ($dec >= 192) { // First byte of a multi byte character
1577
+ // Reduce maxLength to split at start of character
1578
+ $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1579
+ $foundSplitPos = true;
1580
+ } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
1581
+ $lookBack += 3;
1582
+ }
1583
+ } else {
1584
+ // No encoded character found
1585
+ $foundSplitPos = true;
1586
+ }
1587
+ }
1588
+ return $maxLength;
1589
+ }
1590
+
1591
+ /**
1592
+ * Set the body wrapping.
1593
+ * @access public
1594
+ * @return void
1595
+ */
1596
+ public function setWordWrap()
1597
+ {
1598
+ if ($this->WordWrap < 1) {
1599
+ return;
1600
+ }
1601
+
1602
+ switch ($this->message_type) {
1603
+ case 'alt':
1604
+ case 'alt_inline':
1605
+ case 'alt_attach':
1606
+ case 'alt_inline_attach':
1607
+ $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1608
+ break;
1609
+ default:
1610
+ $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1611
+ break;
1612
+ }
1613
+ }
1614
+
1615
+ /**
1616
+ * Assemble message headers.
1617
+ * @access public
1618
+ * @return string The assembled headers
1619
+ */
1620
+ public function createHeader()
1621
+ {
1622
+ $result = '';
1623
+
1624
+ // Set the boundaries
1625
+ $uniq_id = md5(uniqid(time()));
1626
+ $this->boundary[1] = 'b1_' . $uniq_id;
1627
+ $this->boundary[2] = 'b2_' . $uniq_id;
1628
+ $this->boundary[3] = 'b3_' . $uniq_id;
1629
+
1630
+ if ($this->MessageDate == '') {
1631
+ $this->MessageDate = self::rfcDate();
1632
+ }
1633
+ $result .= $this->headerLine('Date', $this->MessageDate);
1634
+
1635
+
1636
+ // To be created automatically by mail()
1637
+ if ($this->SingleTo === true) {
1638
+ if ($this->Mailer != 'mail') {
1639
+ foreach ($this->to as $toaddr) {
1640
+ $this->SingleToArray[] = $this->addrFormat($toaddr);
1641
+ }
1642
+ }
1643
+ } else {
1644
+ if (count($this->to) > 0) {
1645
+ if ($this->Mailer != 'mail') {
1646
+ $result .= $this->addrAppend('To', $this->to);
1647
+ }
1648
+ } elseif (count($this->cc) == 0) {
1649
+ $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1650
+ }
1651
+ }
1652
+
1653
+ $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1654
+
1655
+ // sendmail and mail() extract Cc from the header before sending
1656
+ if (count($this->cc) > 0) {
1657
+ $result .= $this->addrAppend('Cc', $this->cc);
1658
+ }
1659
+
1660
+ // sendmail and mail() extract Bcc from the header before sending
1661
+ if ((
1662
+ $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1663
+ )
1664
+ and count($this->bcc) > 0
1665
+ ) {
1666
+ $result .= $this->addrAppend('Bcc', $this->bcc);
1667
+ }
1668
+
1669
+ if (count($this->ReplyTo) > 0) {
1670
+ $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1671
+ }
1672
+
1673
+ // mail() sets the subject itself
1674
+ if ($this->Mailer != 'mail') {
1675
+ $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1676
+ }
1677
+
1678
+ if ($this->MessageID != '') {
1679
+ $this->lastMessageID = $this->MessageID;
1680
+ } else {
1681
+ $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());
1682
+ }
1683
+ $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
1684
+ $result .= $this->headerLine('X-Priority', $this->Priority);
1685
+ if ($this->XMailer == '') {
1686
+ $result .= $this->headerLine(
1687
+ 'X-Mailer',
1688
+ 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
1689
+ );
1690
+ } else {
1691
+ $myXmailer = trim($this->XMailer);
1692
+ if ($myXmailer) {
1693
+ $result .= $this->headerLine('X-Mailer', $myXmailer);
1694
+ }
1695
+ }
1696
+
1697
+ if ($this->ConfirmReadingTo != '') {
1698
+ $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1699
+ }
1700
+
1701
+ // Add custom headers
1702
+ for ($index = 0; $index < count($this->CustomHeader); $index++) {
1703
+ $result .= $this->headerLine(
1704
+ trim($this->CustomHeader[$index][0]),
1705
+ $this->encodeHeader(trim($this->CustomHeader[$index][1]))
1706
+ );
1707
+ }
1708
+ if (!$this->sign_key_file) {
1709
+ $result .= $this->headerLine('MIME-Version', '1.0');
1710
+ $result .= $this->getMailMIME();
1711
+ }
1712
+
1713
+ return $result;
1714
+ }
1715
+
1716
+ /**
1717
+ * Get the message MIME type headers.
1718
+ * @access public
1719
+ * @return string
1720
+ */
1721
+ public function getMailMIME()
1722
+ {
1723
+ $result = '';
1724
+ $ismultipart = true;
1725
+ switch ($this->message_type) {
1726
+ case 'inline':
1727
+ $result .= $this->headerLine('Content-Type', 'multipart/related;');
1728
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1729
+ break;
1730
+ case 'attach':
1731
+ case 'inline_attach':
1732
+ case 'alt_attach':
1733
+ case 'alt_inline_attach':
1734
+ $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
1735
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1736
+ break;
1737
+ case 'alt':
1738
+ case 'alt_inline':
1739
+ $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
1740
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1741
+ break;
1742
+ default:
1743
+ // Catches case 'plain': and case '':
1744
+ $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
1745
+ $ismultipart = false;
1746
+ break;
1747
+ }
1748
+ // RFC1341 part 5 says 7bit is assumed if not specified
1749
+ if ($this->Encoding != '7bit') {
1750
+ // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
1751
+ if ($ismultipart) {
1752
+ if ($this->Encoding == '8bit') {
1753
+ $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
1754
+ }
1755
+ // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
1756
+ } else {
1757
+ $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
1758
+ }
1759
+ }
1760
+
1761
+ if ($this->Mailer != 'mail') {
1762
+ $result .= $this->LE;
1763
+ }
1764
+
1765
+ return $result;
1766
+ }
1767
+
1768
+ /**
1769
+ * Returns the whole MIME message.
1770
+ * Includes complete headers and body.
1771
+ * Only valid post preSend().
1772
+ * @see PHPMailer::preSend()
1773
+ * @access public
1774
+ * @return string
1775
+ */
1776
+ public function getSentMIMEMessage()
1777
+ {
1778
+ return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
1779
+ }
1780
+
1781
+
1782
+ /**
1783
+ * Assemble the message body.
1784
+ * Returns an empty string on failure.
1785
+ * @access public
1786
+ * @throws phpmailerException
1787
+ * @return string The assembled message body
1788
+ */
1789
+ public function createBody()
1790
+ {
1791
+ $body = '';
1792
+
1793
+ if ($this->sign_key_file) {
1794
+ $body .= $this->getMailMIME() . $this->LE;
1795
+ }
1796
+
1797
+ $this->setWordWrap();
1798
+
1799
+ $bodyEncoding = $this->Encoding;
1800
+ $bodyCharSet = $this->CharSet;
1801
+ if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
1802
+ $bodyEncoding = '7bit';
1803
+ $bodyCharSet = 'us-ascii';
1804
+ }
1805
+ $altBodyEncoding = $this->Encoding;
1806
+ $altBodyCharSet = $this->CharSet;
1807
+ if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
1808
+ $altBodyEncoding = '7bit';
1809
+ $altBodyCharSet = 'us-ascii';
1810
+ }
1811
+ switch ($this->message_type) {
1812
+ case 'inline':
1813
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1814
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
1815
+ $body .= $this->LE . $this->LE;
1816
+ $body .= $this->attachAll('inline', $this->boundary[1]);
1817
+ break;
1818
+ case 'attach':
1819
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1820
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
1821
+ $body .= $this->LE . $this->LE;
1822
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
1823
+ break;
1824
+ case 'inline_attach':
1825
+ $body .= $this->textLine('--' . $this->boundary[1]);
1826
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
1827
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1828
+ $body .= $this->LE;
1829
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
1830
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
1831
+ $body .= $this->LE . $this->LE;
1832
+ $body .= $this->attachAll('inline', $this->boundary[2]);
1833
+ $body .= $this->LE;
1834
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
1835
+ break;
1836
+ case 'alt':
1837
+ $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1838
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1839
+ $body .= $this->LE . $this->LE;
1840
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
1841
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
1842
+ $body .= $this->LE . $this->LE;
1843
+ if (!empty($this->Ical)) {
1844
+ $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
1845
+ $body .= $this->encodeString($this->Ical, $this->Encoding);
1846
+ $body .= $this->LE . $this->LE;
1847
+ }
1848
+ $body .= $this->endBoundary($this->boundary[1]);
1849
+ break;
1850
+ case 'alt_inline':
1851
+ $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1852
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1853
+ $body .= $this->LE . $this->LE;
1854
+ $body .= $this->textLine('--' . $this->boundary[1]);
1855
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
1856
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1857
+ $body .= $this->LE;
1858
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
1859
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
1860
+ $body .= $this->LE . $this->LE;
1861
+ $body .= $this->attachAll('inline', $this->boundary[2]);
1862
+ $body .= $this->LE;
1863
+ $body .= $this->endBoundary($this->boundary[1]);
1864
+ break;
1865
+ case 'alt_attach':
1866
+ $body .= $this->textLine('--' . $this->boundary[1]);
1867
+ $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1868
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1869
+ $body .= $this->LE;
1870
+ $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1871
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1872
+ $body .= $this->LE . $this->LE;
1873
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
1874
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
1875
+ $body .= $this->LE . $this->LE;
1876
+ $body .= $this->endBoundary($this->boundary[2]);
1877
+ $body .= $this->LE;
1878
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
1879
+ break;
1880
+ case 'alt_inline_attach':
1881
+ $body .= $this->textLine('--' . $this->boundary[1]);
1882
+ $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1883
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1884
+ $body .= $this->LE;
1885
+ $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1886
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1887
+ $body .= $this->LE . $this->LE;
1888
+ $body .= $this->textLine('--' . $this->boundary[2]);
1889
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
1890
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
1891
+ $body .= $this->LE;
1892
+ $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
1893
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
1894
+ $body .= $this->LE . $this->LE;
1895
+ $body .= $this->attachAll('inline', $this->boundary[3]);
1896
+ $body .= $this->LE;
1897
+ $body .= $this->endBoundary($this->boundary[2]);
1898
+ $body .= $this->LE;
1899
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
1900
+ break;
1901
+ default:
1902
+ // catch case 'plain' and case ''
1903
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
1904
+ break;
1905
+ }
1906
+
1907
+ if ($this->isError()) {
1908
+ $body = '';
1909
+ } elseif ($this->sign_key_file) {
1910
+ try {
1911
+ if (!defined('PKCS7_TEXT')) {
1912
+ throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
1913
+ }
1914
+ // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
1915
+ $file = tempnam(sys_get_temp_dir(), 'mail');
1916
+ file_put_contents($file, $body); // @TODO check this worked
1917
+ $signed = tempnam(sys_get_temp_dir(), 'signed');
1918
+ if (@openssl_pkcs7_sign(
1919
+ $file,
1920
+ $signed,
1921
+ 'file://' . realpath($this->sign_cert_file),
1922
+ array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
1923
+ null
1924
+ )
1925
+ ) {
1926
+ @unlink($file);
1927
+ $body = file_get_contents($signed);
1928
+ @unlink($signed);
1929
+ } else {
1930
+ @unlink($file);
1931
+ @unlink($signed);
1932
+ throw new phpmailerException($this->lang('signing') . openssl_error_string());
1933
+ }
1934
+ } catch (phpmailerException $exc) {
1935
+ $body = '';
1936
+ if ($this->exceptions) {
1937
+ throw $exc;
1938
+ }
1939
+ }
1940
+ }
1941
+ return $body;
1942
+ }
1943
+
1944
+ /**
1945
+ * Return the start of a message boundary.
1946
+ * @access protected
1947
+ * @param string $boundary
1948
+ * @param string $charSet
1949
+ * @param string $contentType
1950
+ * @param string $encoding
1951
+ * @return string
1952
+ */
1953
+ protected function getBoundary($boundary, $charSet, $contentType, $encoding)
1954
+ {
1955
+ $result = '';
1956
+ if ($charSet == '') {
1957
+ $charSet = $this->CharSet;
1958
+ }
1959
+ if ($contentType == '') {
1960
+ $contentType = $this->ContentType;
1961
+ }
1962
+ if ($encoding == '') {
1963
+ $encoding = $this->Encoding;
1964
+ }
1965
+ $result .= $this->textLine('--' . $boundary);
1966
+ $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
1967
+ $result .= $this->LE;
1968
+ // RFC1341 part 5 says 7bit is assumed if not specified
1969
+ if ($encoding != '7bit') {
1970
+ $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
1971
+ }
1972
+ $result .= $this->LE;
1973
+
1974
+ return $result;
1975
+ }
1976
+
1977
+ /**
1978
+ * Return the end of a message boundary.
1979
+ * @access protected
1980
+ * @param string $boundary
1981
+ * @return string
1982
+ */
1983
+ protected function endBoundary($boundary)
1984
+ {
1985
+ return $this->LE . '--' . $boundary . '--' . $this->LE;
1986
+ }
1987
+
1988
+ /**
1989
+ * Set the message type.
1990
+ * PHPMailer only supports some preset message types,
1991
+ * not arbitrary MIME structures.
1992
+ * @access protected
1993
+ * @return void
1994
+ */
1995
+ protected function setMessageType()
1996
+ {
1997
+ $this->message_type = array();
1998
+ if ($this->alternativeExists()) {
1999
+ $this->message_type[] = 'alt';
2000
+ }
2001
+ if ($this->inlineImageExists()) {
2002
+ $this->message_type[] = 'inline';
2003
+ }
2004
+ if ($this->attachmentExists()) {
2005
+ $this->message_type[] = 'attach';
2006
+ }
2007
+ $this->message_type = implode('_', $this->message_type);
2008
+ if ($this->message_type == '') {
2009
+ $this->message_type = 'plain';
2010
+ }
2011
+ }
2012
+
2013
+ /**
2014
+ * Format a header line.
2015
+ * @access public
2016
+ * @param string $name
2017
+ * @param string $value
2018
+ * @return string
2019
+ */
2020
+ public function headerLine($name, $value)
2021
+ {
2022
+ return $name . ': ' . $value . $this->LE;
2023
+ }
2024
+
2025
+ /**
2026
+ * Return a formatted mail line.
2027
+ * @access public
2028
+ * @param string $value
2029
+ * @return string
2030
+ */
2031
+ public function textLine($value)
2032
+ {
2033
+ return $value . $this->LE;
2034
+ }
2035
+
2036
+ /**
2037
+ * Add an attachment from a path on the filesystem.
2038
+ * Returns false if the file could not be found or read.
2039
+ * @param string $path Path to the attachment.
2040
+ * @param string $name Overrides the attachment name.
2041
+ * @param string $encoding File encoding (see $Encoding).
2042
+ * @param string $type File extension (MIME) type.
2043
+ * @param string $disposition Disposition to use
2044
+ * @throws phpmailerException
2045
+ * @return boolean
2046
+ */
2047
+ public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2048
+ {
2049
+ try {
2050
+ if (!@is_file($path)) {
2051
+ throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2052
+ }
2053
+
2054
+ // If a MIME type is not specified, try to work it out from the file name
2055
+ if ($type == '') {
2056
+ $type = self::filenameToType($path);
2057
+ }
2058
+
2059
+ $filename = basename($path);
2060
+ if ($name == '') {
2061
+ $name = $filename;
2062
+ }
2063
+
2064
+ $this->attachment[] = array(
2065
+ 0 => $path,
2066
+ 1 => $filename,
2067
+ 2 => $name,
2068
+ 3 => $encoding,
2069
+ 4 => $type,
2070
+ 5 => false, // isStringAttachment
2071
+ 6 => $disposition,
2072
+ 7 => 0
2073
+ );
2074
+
2075
+ } catch (phpmailerException $exc) {
2076
+ $this->setError($exc->getMessage());
2077
+ $this->edebug($exc->getMessage());
2078
+ if ($this->exceptions) {
2079
+ throw $exc;
2080
+ }
2081
+ return false;
2082
+ }
2083
+ return true;
2084
+ }
2085
+
2086
+ /**
2087
+ * Return the array of attachments.
2088
+ * @return array
2089
+ */
2090
+ public function getAttachments()
2091
+ {
2092
+ return $this->attachment;
2093
+ }
2094
+
2095
+ /**
2096
+ * Attach all file, string, and binary attachments to the message.
2097
+ * Returns an empty string on failure.
2098
+ * @access protected
2099
+ * @param string $disposition_type
2100
+ * @param string $boundary
2101
+ * @return string
2102
+ */
2103
+ protected function attachAll($disposition_type, $boundary)
2104
+ {
2105
+ // Return text of body
2106
+ $mime = array();
2107
+ $cidUniq = array();
2108
+ $incl = array();
2109
+
2110
+ // Add all attachments
2111
+ foreach ($this->attachment as $attachment) {
2112
+ // Check if it is a valid disposition_filter
2113
+ if ($attachment[6] == $disposition_type) {
2114
+ // Check for string attachment
2115
+ $string = '';
2116
+ $path = '';
2117
+ $bString = $attachment[5];
2118
+ if ($bString) {
2119
+ $string = $attachment[0];
2120
+ } else {
2121
+ $path = $attachment[0];
2122
+ }
2123
+
2124
+ $inclhash = md5(serialize($attachment));
2125
+ if (in_array($inclhash, $incl)) {
2126
+ continue;
2127
+ }
2128
+ $incl[] = $inclhash;
2129
+ $name = $attachment[2];
2130
+ $encoding = $attachment[3];
2131
+ $type = $attachment[4];
2132
+ $disposition = $attachment[6];
2133
+ $cid = $attachment[7];
2134
+ if ($disposition == 'inline' && isset($cidUniq[$cid])) {
2135
+ continue;
2136
+ }
2137
+ $cidUniq[$cid] = true;
2138
+
2139
+ $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2140
+ $mime[] = sprintf(
2141
+ 'Content-Type: %s; name="%s"%s',
2142
+ $type,
2143
+ $this->encodeHeader($this->secureHeader($name)),
2144
+ $this->LE
2145
+ );
2146
+ // RFC1341 part 5 says 7bit is assumed if not specified
2147
+ if ($encoding != '7bit') {
2148
+ $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2149
+ }
2150
+
2151
+ if ($disposition == 'inline') {
2152
+ $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2153
+ }
2154
+
2155
+ // If a filename contains any of these chars, it should be quoted,
2156
+ // but not otherwise: RFC2183 & RFC2045 5.1
2157
+ // Fixes a warning in IETF's msglint MIME checker
2158
+ // Allow for bypassing the Content-Disposition header totally
2159
+ if (!(empty($disposition))) {
2160
+ if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
2161
+ $mime[] = sprintf(
2162
+ 'Content-Disposition: %s; filename="%s"%s',
2163
+ $disposition,
2164
+ $this->encodeHeader($this->secureHeader($name)),
2165
+ $this->LE . $this->LE
2166
+ );
2167
+ } else {
2168
+ $mime[] = sprintf(
2169
+ 'Content-Disposition: %s; filename=%s%s',
2170
+ $disposition,
2171
+ $this->encodeHeader($this->secureHeader($name)),
2172
+ $this->LE . $this->LE
2173
+ );
2174
+ }
2175
+ } else {
2176
+ $mime[] = $this->LE;
2177
+ }
2178
+
2179
+ // Encode as string attachment
2180
+ if ($bString) {
2181
+ $mime[] = $this->encodeString($string, $encoding);
2182
+ if ($this->isError()) {
2183
+ return '';
2184
+ }
2185
+ $mime[] = $this->LE . $this->LE;
2186
+ } else {
2187
+ $mime[] = $this->encodeFile($path, $encoding);
2188
+ if ($this->isError()) {
2189
+ return '';
2190
+ }
2191
+ $mime[] = $this->LE . $this->LE;
2192
+ }
2193
+ }
2194
+ }
2195
+
2196
+ $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2197
+
2198
+ return implode('', $mime);
2199
+ }
2200
+
2201
+ /**
2202
+ * Encode a file attachment in requested format.
2203
+ * Returns an empty string on failure.
2204
+ * @param string $path The full path to the file
2205
+ * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2206
+ * @throws phpmailerException
2207
+ * @see EncodeFile(encodeFile
2208
+ * @access protected
2209
+ * @return string
2210
+ */
2211
+ protected function encodeFile($path, $encoding = 'base64')
2212
+ {
2213
+ try {
2214
+ if (!is_readable($path)) {
2215
+ throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2216
+ }
2217
+ $magic_quotes = get_magic_quotes_runtime();
2218
+ if ($magic_quotes) {
2219
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2220
+ set_magic_quotes_runtime(false);
2221
+ } else {
2222
+ //Doesn't exist in PHP 5.4, but we don't need to check because
2223
+ //get_magic_quotes_runtime always returns false in 5.4+
2224
+ //so it will never get here
2225
+ ini_set('magic_quotes_runtime', 0);
2226
+ }
2227
+ }
2228
+ $file_buffer = file_get_contents($path);
2229
+ $file_buffer = $this->encodeString($file_buffer, $encoding);
2230
+ if ($magic_quotes) {
2231
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2232
+ set_magic_quotes_runtime($magic_quotes);
2233
+ } else {
2234
+ ini_set('magic_quotes_runtime', ($magic_quotes?'1':'0'));
2235
+ }
2236
+ }
2237
+ return $file_buffer;
2238
+ } catch (Exception $exc) {
2239
+ $this->setError($exc->getMessage());
2240
+ return '';
2241
+ }
2242
+ }
2243
+
2244
+ /**
2245
+ * Encode a string in requested format.
2246
+ * Returns an empty string on failure.
2247
+ * @param string $str The text to encode
2248
+ * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2249
+ * @access public
2250
+ * @return string
2251
+ */
2252
+ public function encodeString($str, $encoding = 'base64')
2253
+ {
2254
+ $encoded = '';
2255
+ switch (strtolower($encoding)) {
2256
+ case 'base64':
2257
+ $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2258
+ break;
2259
+ case '7bit':
2260
+ case '8bit':
2261
+ $encoded = $this->fixEOL($str);
2262
+ // Make sure it ends with a line break
2263
+ if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2264
+ $encoded .= $this->LE;
2265
+ }
2266
+ break;
2267
+ case 'binary':
2268
+ $encoded = $str;
2269
+ break;
2270
+ case 'quoted-printable':
2271
+ $encoded = $this->encodeQP($str);
2272
+ break;
2273
+ default:
2274
+ $this->setError($this->lang('encoding') . $encoding);
2275
+ break;
2276
+ }
2277
+ return $encoded;
2278
+ }
2279
+
2280
+ /**
2281
+ * Encode a header string optimally.
2282
+ * Picks shortest of Q, B, quoted-printable or none.
2283
+ * @access public
2284
+ * @param string $str
2285
+ * @param string $position
2286
+ * @return string
2287
+ */
2288
+ public function encodeHeader($str, $position = 'text')
2289
+ {
2290
+ $matchcount = 0;
2291
+ switch (strtolower($position)) {
2292
+ case 'phrase':
2293
+ if (!preg_match('/[\200-\377]/', $str)) {
2294
+ // Can't use addslashes as we don't know the value of magic_quotes_sybase
2295
+ $encoded = addcslashes($str, "\0..\37\177\\\"");
2296
+ if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2297
+ return ($encoded);
2298
+ } else {
2299
+ return ("\"$encoded\"");
2300
+ }
2301
+ }
2302
+ $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2303
+ break;
2304
+ /** @noinspection PhpMissingBreakStatementInspection */
2305
+ case 'comment':
2306
+ $matchcount = preg_match_all('/[()"]/', $str, $matches);
2307
+ // Intentional fall-through
2308
+ case 'text':
2309
+ default:
2310
+ $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2311
+ break;
2312
+ }
2313
+
2314
+ if ($matchcount == 0) { // There are no chars that need encoding
2315
+ return ($str);
2316
+ }
2317
+
2318
+ $maxlen = 75 - 7 - strlen($this->CharSet);
2319
+ // Try to select the encoding which should produce the shortest output
2320
+ if ($matchcount > strlen($str) / 3) {
2321
+ // More than a third of the content will need encoding, so B encoding will be most efficient
2322
+ $encoding = 'B';
2323
+ if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2324
+ // Use a custom function which correctly encodes and wraps long
2325
+ // multibyte strings without breaking lines within a character
2326
+ $encoded = $this->base64EncodeWrapMB($str, "\n");
2327
+ } else {
2328
+ $encoded = base64_encode($str);
2329
+ $maxlen -= $maxlen % 4;
2330
+ $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2331
+ }
2332
+ } else {
2333
+ $encoding = 'Q';
2334
+ $encoded = $this->encodeQ($str, $position);
2335
+ $encoded = $this->wrapText($encoded, $maxlen, true);
2336
+ $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2337
+ }
2338
+
2339
+ $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2340
+ $encoded = trim(str_replace("\n", $this->LE, $encoded));
2341
+
2342
+ return $encoded;
2343
+ }
2344
+
2345
+ /**
2346
+ * Check if a string contains multi-byte characters.
2347
+ * @access public
2348
+ * @param string $str multi-byte text to wrap encode
2349
+ * @return boolean
2350
+ */
2351
+ public function hasMultiBytes($str)
2352
+ {
2353
+ if (function_exists('mb_strlen')) {
2354
+ return (strlen($str) > mb_strlen($str, $this->CharSet));
2355
+ } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2356
+ return false;
2357
+ }
2358
+ }
2359
+
2360
+ /**
2361
+ * Does a string contain any 8-bit chars (in any charset)?
2362
+ * @param string $text
2363
+ * @return boolean
2364
+ */
2365
+ public function has8bitChars($text)
2366
+ {
2367
+ return (boolean)preg_match('/[\x80-\xFF]/', $text);
2368
+ }
2369
+
2370
+ /**
2371
+ * Encode and wrap long multibyte strings for mail headers
2372
+ * without breaking lines within a character.
2373
+ * Adapted from a function by paravoid
2374
+ * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2375
+ * @access public
2376
+ * @param string $str multi-byte text to wrap encode
2377
+ * @param string $linebreak string to use as linefeed/end-of-line
2378
+ * @return string
2379
+ */
2380
+ public function base64EncodeWrapMB($str, $linebreak = null)
2381
+ {
2382
+ $start = '=?' . $this->CharSet . '?B?';
2383
+ $end = '?=';
2384
+ $encoded = '';
2385
+ if ($linebreak === null) {
2386
+ $linebreak = $this->LE;
2387
+ }
2388
+
2389
+ $mb_length = mb_strlen($str, $this->CharSet);
2390
+ // Each line must have length <= 75, including $start and $end
2391
+ $length = 75 - strlen($start) - strlen($end);
2392
+ // Average multi-byte ratio
2393
+ $ratio = $mb_length / strlen($str);
2394
+ // Base64 has a 4:3 ratio
2395
+ $avgLength = floor($length * $ratio * .75);
2396
+
2397
+ for ($i = 0; $i < $mb_length; $i += $offset) {
2398
+ $lookBack = 0;
2399
+ do {
2400
+ $offset = $avgLength - $lookBack;
2401
+ $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2402
+ $chunk = base64_encode($chunk);
2403
+ $lookBack++;
2404
+ } while (strlen($chunk) > $length);
2405
+ $encoded .= $chunk . $linebreak;
2406
+ }
2407
+
2408
+ // Chomp the last linefeed
2409
+ $encoded = substr($encoded, 0, -strlen($linebreak));
2410
+ return $encoded;
2411
+ }
2412
+
2413
+ /**
2414
+ * Encode a string in quoted-printable format.
2415
+ * According to RFC2045 section 6.7.
2416
+ * @access public
2417
+ * @param string $string The text to encode
2418
+ * @param integer $line_max Number of chars allowed on a line before wrapping
2419
+ * @return string
2420
+ * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2421
+ */
2422
+ public function encodeQP($string, $line_max = 76)
2423
+ {
2424
+ if (function_exists('quoted_printable_encode')) { // Use native function if it's available (>= PHP5.3)
2425
+ return $this->fixEOL(quoted_printable_encode($string));
2426
+ }
2427
+ // Fall back to a pure PHP implementation
2428
+ $string = str_replace(
2429
+ array('%20', '%0D%0A.', '%0D%0A', '%'),
2430
+ array(' ', "\r\n=2E", "\r\n", '='),
2431
+ rawurlencode($string)
2432
+ );
2433
+ $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2434
+ return $this->fixEOL($string);
2435
+ }
2436
+
2437
+ /**
2438
+ * Backward compatibility wrapper for an old QP encoding function that was removed.
2439
+ * @see PHPMailer::encodeQP()
2440
+ * @access public
2441
+ * @param string $string
2442
+ * @param integer $line_max
2443
+ * @param boolean $space_conv
2444
+ * @return string
2445
+ * @deprecated Use encodeQP instead.
2446
+ */
2447
+ public function encodeQPphp(
2448
+ $string,
2449
+ $line_max = 76,
2450
+ /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2451
+ ) {
2452
+ return $this->encodeQP($string, $line_max);
2453
+ }
2454
+
2455
+ /**
2456
+ * Encode a string using Q encoding.
2457
+ * @link http://tools.ietf.org/html/rfc2047
2458
+ * @param string $str the text to encode
2459
+ * @param string $position Where the text is going to be used, see the RFC for what that means
2460
+ * @access public
2461
+ * @return string
2462
+ */
2463
+ public function encodeQ($str, $position = 'text')
2464
+ {
2465
+ // There should not be any EOL in the string
2466
+ $pattern = '';
2467
+ $encoded = str_replace(array("\r", "\n"), '', $str);
2468
+ switch (strtolower($position)) {
2469
+ case 'phrase':
2470
+ // RFC 2047 section 5.3
2471
+ $pattern = '^A-Za-z0-9!*+\/ -';
2472
+ break;
2473
+ /** @noinspection PhpMissingBreakStatementInspection */
2474
+ case 'comment':
2475
+ // RFC 2047 section 5.2
2476
+ $pattern = '\(\)"';
2477
+ // intentional fall-through
2478
+ // for this reason we build the $pattern without including delimiters and []
2479
+ case 'text':
2480
+ default:
2481
+ // RFC 2047 section 5.1
2482
+ // Replace every high ascii, control, =, ? and _ characters
2483
+ $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2484
+ break;
2485
+ }
2486
+ $matches = array();
2487
+ if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2488
+ // If the string contains an '=', make sure it's the first thing we replace
2489
+ // so as to avoid double-encoding
2490
+ $eqkey = array_search('=', $matches[0]);
2491
+ if ($eqkey !== false) {
2492
+ unset($matches[0][$eqkey]);
2493
+ array_unshift($matches[0], '=');
2494
+ }
2495
+ foreach (array_unique($matches[0]) as $char) {
2496
+ $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2497
+ }
2498
+ }
2499
+ // Replace every spaces to _ (more readable than =20)
2500
+ return str_replace(' ', '_', $encoded);
2501
+ }
2502
+
2503
+
2504
+ /**
2505
+ * Add a string or binary attachment (non-filesystem).
2506
+ * This method can be used to attach ascii or binary data,
2507
+ * such as a BLOB record from a database.
2508
+ * @param string $string String attachment data.
2509
+ * @param string $filename Name of the attachment.
2510
+ * @param string $encoding File encoding (see $Encoding).
2511
+ * @param string $type File extension (MIME) type.
2512
+ * @param string $disposition Disposition to use
2513
+ * @return void
2514
+ */
2515
+ public function addStringAttachment(
2516
+ $string,
2517
+ $filename,
2518
+ $encoding = 'base64',
2519
+ $type = '',
2520
+ $disposition = 'attachment'
2521
+ ) {
2522
+ // If a MIME type is not specified, try to work it out from the file name
2523
+ if ($type == '') {
2524
+ $type = self::filenameToType($filename);
2525
+ }
2526
+ // Append to $attachment array
2527
+ $this->attachment[] = array(
2528
+ 0 => $string,
2529
+ 1 => $filename,
2530
+ 2 => basename($filename),
2531
+ 3 => $encoding,
2532
+ 4 => $type,
2533
+ 5 => true, // isStringAttachment
2534
+ 6 => $disposition,
2535
+ 7 => 0
2536
+ );
2537
+ }
2538
+
2539
+ /**
2540
+ * Add an embedded (inline) attachment from a file.
2541
+ * This can include images, sounds, and just about any other document type.
2542
+ * These differ from 'regular' attachmants in that they are intended to be
2543
+ * displayed inline with the message, not just attached for download.
2544
+ * This is used in HTML messages that embed the images
2545
+ * the HTML refers to using the $cid value.
2546
+ * @param string $path Path to the attachment.
2547
+ * @param string $cid Content ID of the attachment; Use this to reference
2548
+ * the content when using an embedded image in HTML.
2549
+ * @param string $name Overrides the attachment name.
2550
+ * @param string $encoding File encoding (see $Encoding).
2551
+ * @param string $type File MIME type.
2552
+ * @param string $disposition Disposition to use
2553
+ * @return boolean True on successfully adding an attachment
2554
+ */
2555
+ public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2556
+ {
2557
+ if (!@is_file($path)) {
2558
+ $this->setError($this->lang('file_access') . $path);
2559
+ return false;
2560
+ }
2561
+
2562
+ // If a MIME type is not specified, try to work it out from the file name
2563
+ if ($type == '') {
2564
+ $type = self::filenameToType($path);
2565
+ }
2566
+
2567
+ $filename = basename($path);
2568
+ if ($name == '') {
2569
+ $name = $filename;
2570
+ }
2571
+
2572
+ // Append to $attachment array
2573
+ $this->attachment[] = array(
2574
+ 0 => $path,
2575
+ 1 => $filename,
2576
+ 2 => $name,
2577
+ 3 => $encoding,
2578
+ 4 => $type,
2579
+ 5 => false, // isStringAttachment
2580
+ 6 => $disposition,
2581
+ 7 => $cid
2582
+ );
2583
+ return true;
2584
+ }
2585
+
2586
+ /**
2587
+ * Add an embedded stringified attachment.
2588
+ * This can include images, sounds, and just about any other document type.
2589
+ * Be sure to set the $type to an image type for images:
2590
+ * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2591
+ * @param string $string The attachment binary data.
2592
+ * @param string $cid Content ID of the attachment; Use this to reference
2593
+ * the content when using an embedded image in HTML.
2594
+ * @param string $name
2595
+ * @param string $encoding File encoding (see $Encoding).
2596
+ * @param string $type MIME type.
2597
+ * @param string $disposition Disposition to use
2598
+ * @return boolean True on successfully adding an attachment
2599
+ */
2600
+ public function addStringEmbeddedImage(
2601
+ $string,
2602
+ $cid,
2603
+ $name = '',
2604
+ $encoding = 'base64',
2605
+ $type = '',
2606
+ $disposition = 'inline'
2607
+ ) {
2608
+ // If a MIME type is not specified, try to work it out from the name
2609
+ if ($type == '') {
2610
+ $type = self::filenameToType($name);
2611
+ }
2612
+
2613
+ // Append to $attachment array
2614
+ $this->attachment[] = array(
2615
+ 0 => $string,
2616
+ 1 => $name,
2617
+ 2 => $name,
2618
+ 3 => $encoding,
2619
+ 4 => $type,
2620
+ 5 => true, // isStringAttachment
2621
+ 6 => $disposition,
2622
+ 7 => $cid
2623
+ );
2624
+ return true;
2625
+ }
2626
+
2627
+ /**
2628
+ * Check if an inline attachment is present.
2629
+ * @access public
2630
+ * @return boolean
2631
+ */
2632
+ public function inlineImageExists()
2633
+ {
2634
+ foreach ($this->attachment as $attachment) {
2635
+ if ($attachment[6] == 'inline') {
2636
+ return true;
2637
+ }
2638
+ }
2639
+ return false;
2640
+ }
2641
+
2642
+ /**
2643
+ * Check if an attachment (non-inline) is present.
2644
+ * @return boolean
2645
+ */
2646
+ public function attachmentExists()
2647
+ {
2648
+ foreach ($this->attachment as $attachment) {
2649
+ if ($attachment[6] == 'attachment') {
2650
+ return true;
2651
+ }
2652
+ }
2653
+ return false;
2654
+ }
2655
+
2656
+ /**
2657
+ * Check if this message has an alternative body set.
2658
+ * @return boolean
2659
+ */
2660
+ public function alternativeExists()
2661
+ {
2662
+ return !empty($this->AltBody);
2663
+ }
2664
+
2665
+ /**
2666
+ * Clear all To recipients.
2667
+ * @return void
2668
+ */
2669
+ public function clearAddresses()
2670
+ {
2671
+ foreach ($this->to as $to) {
2672
+ unset($this->all_recipients[strtolower($to[0])]);
2673
+ }
2674
+ $this->to = array();
2675
+ }
2676
+
2677
+ /**
2678
+ * Clear all CC recipients.
2679
+ * @return void
2680
+ */
2681
+ public function clearCCs()
2682
+ {
2683
+ foreach ($this->cc as $cc) {
2684
+ unset($this->all_recipients[strtolower($cc[0])]);
2685
+ }
2686
+ $this->cc = array();
2687
+ }
2688
+
2689
+ /**
2690
+ * Clear all BCC recipients.
2691
+ * @return void
2692
+ */
2693
+ public function clearBCCs()
2694
+ {
2695
+ foreach ($this->bcc as $bcc) {
2696
+ unset($this->all_recipients[strtolower($bcc[0])]);
2697
+ }
2698
+ $this->bcc = array();
2699
+ }
2700
+
2701
+ /**
2702
+ * Clear all ReplyTo recipients.
2703
+ * @return void
2704
+ */
2705
+ public function clearReplyTos()
2706
+ {
2707
+ $this->ReplyTo = array();
2708
+ }
2709
+
2710
+ /**
2711
+ * Clear all recipient types.
2712
+ * @return void
2713
+ */
2714
+ public function clearAllRecipients()
2715
+ {
2716
+ $this->to = array();
2717
+ $this->cc = array();
2718
+ $this->bcc = array();
2719
+ $this->all_recipients = array();
2720
+ }
2721
+
2722
+ /**
2723
+ * Clear all filesystem, string, and binary attachments.
2724
+ * @return void
2725
+ */
2726
+ public function clearAttachments()
2727
+ {
2728
+ $this->attachment = array();
2729
+ }
2730
+
2731
+ /**
2732
+ * Clear all custom headers.
2733
+ * @return void
2734
+ */
2735
+ public function clearCustomHeaders()
2736
+ {
2737
+ $this->CustomHeader = array();
2738
+ }
2739
+
2740
+ /**
2741
+ * Add an error message to the error container.
2742
+ * @access protected
2743
+ * @param string $msg
2744
+ * @return void
2745
+ */
2746
+ protected function setError($msg)
2747
+ {
2748
+ $this->error_count++;
2749
+ if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2750
+ $lasterror = $this->smtp->getError();
2751
+ if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
2752
+ $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
2753
+ }
2754
+ }
2755
+ $this->ErrorInfo = $msg;
2756
+ }
2757
+
2758
+ /**
2759
+ * Return an RFC 822 formatted date.
2760
+ * @access public
2761
+ * @return string
2762
+ * @static
2763
+ */
2764
+ public static function rfcDate()
2765
+ {
2766
+ // Set the time zone to whatever the default is to avoid 500 errors
2767
+ // Will default to UTC if it's not set properly in php.ini
2768
+ date_default_timezone_set(@date_default_timezone_get());
2769
+ return date('D, j M Y H:i:s O');
2770
+ }
2771
+
2772
+ /**
2773
+ * Get the server hostname.
2774
+ * Returns 'localhost.localdomain' if unknown.
2775
+ * @access protected
2776
+ * @return string
2777
+ */
2778
+ protected function serverHostname()
2779
+ {
2780
+ $result = 'localhost.localdomain';
2781
+ if (!empty($this->Hostname)) {
2782
+ $result = $this->Hostname;
2783
+ } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
2784
+ $result = $_SERVER['SERVER_NAME'];
2785
+ } elseif (function_exists('gethostname') && gethostname() !== false) {
2786
+ $result = gethostname();
2787
+ } elseif (php_uname('n') !== false) {
2788
+ $result = php_uname('n');
2789
+ }
2790
+ return $result;
2791
+ }
2792
+
2793
+ /**
2794
+ * Get an error message in the current language.
2795
+ * @access protected
2796
+ * @param string $key
2797
+ * @return string
2798
+ */
2799
+ protected function lang($key)
2800
+ {
2801
+ if (count($this->language) < 1) {
2802
+ $this->setLanguage('en'); // set the default language
2803
+ }
2804
+
2805
+ if (isset($this->language[$key])) {
2806
+ return $this->language[$key];
2807
+ } else {
2808
+ return 'Language string failed to load: ' . $key;
2809
+ }
2810
+ }
2811
+
2812
+ /**
2813
+ * Check if an error occurred.
2814
+ * @access public
2815
+ * @return boolean True if an error did occur.
2816
+ */
2817
+ public function isError()
2818
+ {
2819
+ return ($this->error_count > 0);
2820
+ }
2821
+
2822
+ /**
2823
+ * Ensure consistent line endings in a string.
2824
+ * Changes every end of line from CRLF, CR or LF to $this->LE.
2825
+ * @access public
2826
+ * @param string $str String to fixEOL
2827
+ * @return string
2828
+ */
2829
+ public function fixEOL($str)
2830
+ {
2831
+ // Normalise to \n
2832
+ $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
2833
+ // Now convert LE as needed
2834
+ if ($this->LE !== "\n") {
2835
+ $nstr = str_replace("\n", $this->LE, $nstr);
2836
+ }
2837
+ return $nstr;
2838
+ }
2839
+
2840
+ /**
2841
+ * Add a custom header.
2842
+ * $name value can be overloaded to contain
2843
+ * both header name and value (name:value)
2844
+ * @access public
2845
+ * @param string $name Custom header name
2846
+ * @param string $value Header value
2847
+ * @return void
2848
+ */
2849
+ public function addCustomHeader($name, $value = null)
2850
+ {
2851
+ if ($value === null) {
2852
+ // Value passed in as name:value
2853
+ $this->CustomHeader[] = explode(':', $name, 2);
2854
+ } else {
2855
+ $this->CustomHeader[] = array($name, $value);
2856
+ }
2857
+ }
2858
+
2859
+ /**
2860
+ * Create a message from an HTML string.
2861
+ * Automatically makes modifications for inline images and backgrounds
2862
+ * and creates a plain-text version by converting the HTML.
2863
+ * Overwrites any existing values in $this->Body and $this->AltBody
2864
+ * @access public
2865
+ * @param string $message HTML message string
2866
+ * @param string $basedir baseline directory for path
2867
+ * @param boolean $advanced Whether to use the advanced HTML to text converter
2868
+ * @return string $message
2869
+ */
2870
+ public function msgHTML($message, $basedir = '', $advanced = false)
2871
+ {
2872
+ preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
2873
+ if (isset($images[2])) {
2874
+ foreach ($images[2] as $imgindex => $url) {
2875
+ // do not change urls for absolute images (thanks to corvuscorax)
2876
+ if (!preg_match('#^[A-z]+://#', $url)) {
2877
+ $filename = basename($url);
2878
+ $directory = dirname($url);
2879
+ if ($directory == '.') {
2880
+ $directory = '';
2881
+ }
2882
+ $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
2883
+ if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
2884
+ $basedir .= '/';
2885
+ }
2886
+ if (strlen($directory) > 1 && substr($directory, -1) != '/') {
2887
+ $directory .= '/';
2888
+ }
2889
+ if ($this->addEmbeddedImage(
2890
+ $basedir . $directory . $filename,
2891
+ $cid,
2892
+ $filename,
2893
+ 'base64',
2894
+ self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION))
2895
+ )
2896
+ ) {
2897
+ $message = preg_replace(
2898
+ '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
2899
+ $images[1][$imgindex] . '="cid:' . $cid . '"',
2900
+ $message
2901
+ );
2902
+ }
2903
+ }
2904
+ }
2905
+ }
2906
+ $this->isHTML(true);
2907
+ // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
2908
+ $this->Body = $this->normalizeBreaks($message);
2909
+ $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
2910
+ if (empty($this->AltBody)) {
2911
+ $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
2912
+ self::CRLF . self::CRLF;
2913
+ }
2914
+ return $this->Body;
2915
+ }
2916
+
2917
+ /**
2918
+ * Convert an HTML string into plain text.
2919
+ * @param string $html The HTML text to convert
2920
+ * @param boolean $advanced Should this use the more complex html2text converter or just a simple one?
2921
+ * @return string
2922
+ */
2923
+ public function html2text($html, $advanced = false)
2924
+ {
2925
+ if ($advanced) {
2926
+ require_once 'extras/class.html2text.php';
2927
+ $htmlconverter = new html2text($html);
2928
+ return $htmlconverter->get_text();
2929
+ }
2930
+ return html_entity_decode(
2931
+ trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
2932
+ ENT_QUOTES,
2933
+ $this->CharSet
2934
+ );
2935
+ }
2936
+
2937
+ /**
2938
+ * Get the MIME type for a file extension.
2939
+ * @param string $ext File extension
2940
+ * @access public
2941
+ * @return string MIME type of file.
2942
+ * @static
2943
+ */
2944
+ public static function _mime_types($ext = '')
2945
+ {
2946
+ $mimes = array(
2947
+ 'xl' => 'application/excel',
2948
+ 'hqx' => 'application/mac-binhex40',
2949
+ 'cpt' => 'application/mac-compactpro',
2950
+ 'bin' => 'application/macbinary',
2951
+ 'doc' => 'application/msword',
2952
+ 'word' => 'application/msword',
2953
+ 'class' => 'application/octet-stream',
2954
+ 'dll' => 'application/octet-stream',
2955
+ 'dms' => 'application/octet-stream',
2956
+ 'exe' => 'application/octet-stream',
2957
+ 'lha' => 'application/octet-stream',
2958
+ 'lzh' => 'application/octet-stream',
2959
+ 'psd' => 'application/octet-stream',
2960
+ 'sea' => 'application/octet-stream',
2961
+ 'so' => 'application/octet-stream',
2962
+ 'oda' => 'application/oda',
2963
+ 'pdf' => 'application/pdf',
2964
+ 'ai' => 'application/postscript',
2965
+ 'eps' => 'application/postscript',
2966
+ 'ps' => 'application/postscript',
2967
+ 'smi' => 'application/smil',
2968
+ 'smil' => 'application/smil',
2969
+ 'mif' => 'application/vnd.mif',
2970
+ 'xls' => 'application/vnd.ms-excel',
2971
+ 'ppt' => 'application/vnd.ms-powerpoint',
2972
+ 'wbxml' => 'application/vnd.wap.wbxml',
2973
+ 'wmlc' => 'application/vnd.wap.wmlc',
2974
+ 'dcr' => 'application/x-director',
2975
+ 'dir' => 'application/x-director',
2976
+ 'dxr' => 'application/x-director',
2977
+ 'dvi' => 'application/x-dvi',
2978
+ 'gtar' => 'application/x-gtar',
2979
+ 'php3' => 'application/x-httpd-php',
2980
+ 'php4' => 'application/x-httpd-php',
2981
+ 'php' => 'application/x-httpd-php',
2982
+ 'phtml' => 'application/x-httpd-php',
2983
+ 'phps' => 'application/x-httpd-php-source',
2984
+ 'js' => 'application/x-javascript',
2985
+ 'swf' => 'application/x-shockwave-flash',
2986
+ 'sit' => 'application/x-stuffit',
2987
+ 'tar' => 'application/x-tar',
2988
+ 'tgz' => 'application/x-tar',
2989
+ 'xht' => 'application/xhtml+xml',
2990
+ 'xhtml' => 'application/xhtml+xml',
2991
+ 'zip' => 'application/zip',
2992
+ 'mid' => 'audio/midi',
2993
+ 'midi' => 'audio/midi',
2994
+ 'mp2' => 'audio/mpeg',
2995
+ 'mp3' => 'audio/mpeg',
2996
+ 'mpga' => 'audio/mpeg',
2997
+ 'aif' => 'audio/x-aiff',
2998
+ 'aifc' => 'audio/x-aiff',
2999
+ 'aiff' => 'audio/x-aiff',
3000
+ 'ram' => 'audio/x-pn-realaudio',
3001
+ 'rm' => 'audio/x-pn-realaudio',
3002
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
3003
+ 'ra' => 'audio/x-realaudio',
3004
+ 'wav' => 'audio/x-wav',
3005
+ 'bmp' => 'image/bmp',
3006
+ 'gif' => 'image/gif',
3007
+ 'jpeg' => 'image/jpeg',
3008
+ 'jpe' => 'image/jpeg',
3009
+ 'jpg' => 'image/jpeg',
3010
+ 'png' => 'image/png',
3011
+ 'tiff' => 'image/tiff',
3012
+ 'tif' => 'image/tiff',
3013
+ 'eml' => 'message/rfc822',
3014
+ 'css' => 'text/css',
3015
+ 'html' => 'text/html',
3016
+ 'htm' => 'text/html',
3017
+ 'shtml' => 'text/html',
3018
+ 'log' => 'text/plain',
3019
+ 'text' => 'text/plain',
3020
+ 'txt' => 'text/plain',
3021
+ 'rtx' => 'text/richtext',
3022
+ 'rtf' => 'text/rtf',
3023
+ 'vcf' => 'text/vcard',
3024
+ 'vcard' => 'text/vcard',
3025
+ 'xml' => 'text/xml',
3026
+ 'xsl' => 'text/xml',
3027
+ 'mpeg' => 'video/mpeg',
3028
+ 'mpe' => 'video/mpeg',
3029
+ 'mpg' => 'video/mpeg',
3030
+ 'mov' => 'video/quicktime',
3031
+ 'qt' => 'video/quicktime',
3032
+ 'rv' => 'video/vnd.rn-realvideo',
3033
+ 'avi' => 'video/x-msvideo',
3034
+ 'movie' => 'video/x-sgi-movie'
3035
+ );
3036
+ return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
3037
+ }
3038
+
3039
+ /**
3040
+ * Map a file name to a MIME type.
3041
+ * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3042
+ * @param string $filename A file name or full path, does not need to exist as a file
3043
+ * @return string
3044
+ * @static
3045
+ */
3046
+ public static function filenameToType($filename)
3047
+ {
3048
+ // In case the path is a URL, strip any query string before getting extension
3049
+ $qpos = strpos($filename, '?');
3050
+ if ($qpos !== false) {
3051
+ $filename = substr($filename, 0, $qpos);
3052
+ }
3053
+ $pathinfo = self::mb_pathinfo($filename);
3054
+ return self::_mime_types($pathinfo['extension']);
3055
+ }
3056
+
3057
+ /**
3058
+ * Multi-byte-safe pathinfo replacement.
3059
+ * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3060
+ * Works similarly to the one in PHP >= 5.2.0
3061
+ * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3062
+ * @param string $path A filename or path, does not need to exist as a file
3063
+ * @param integer|string $options Either a PATHINFO_* constant,
3064
+ * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3065
+ * @return string|array
3066
+ * @static
3067
+ */
3068
+ public static function mb_pathinfo($path, $options = null)
3069
+ {
3070
+ $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3071
+ $pathinfo = array();
3072
+ if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3073
+ if (array_key_exists(1, $pathinfo)) {
3074
+ $ret['dirname'] = $pathinfo[1];
3075
+ }
3076
+ if (array_key_exists(2, $pathinfo)) {
3077
+ $ret['basename'] = $pathinfo[2];
3078
+ }
3079
+ if (array_key_exists(5, $pathinfo)) {
3080
+ $ret['extension'] = $pathinfo[5];
3081
+ }
3082
+ if (array_key_exists(3, $pathinfo)) {
3083
+ $ret['filename'] = $pathinfo[3];
3084
+ }
3085
+ }
3086
+ switch ($options) {
3087
+ case PATHINFO_DIRNAME:
3088
+ case 'dirname':
3089
+ return $ret['dirname'];
3090
+ case PATHINFO_BASENAME:
3091
+ case 'basename':
3092
+ return $ret['basename'];
3093
+ case PATHINFO_EXTENSION:
3094
+ case 'extension':
3095
+ return $ret['extension'];
3096
+ case PATHINFO_FILENAME:
3097
+ case 'filename':
3098
+ return $ret['filename'];
3099
+ default:
3100
+ return $ret;
3101
+ }
3102
+ }
3103
+
3104
+ /**
3105
+ * Set or reset instance properties.
3106
+ *
3107
+ * Usage Example:
3108
+ * $page->set('X-Priority', '3');
3109
+ *
3110
+ * @access public
3111
+ * @param string $name
3112
+ * @param mixed $value
3113
+ * NOTE: will not work with arrays, there are no arrays to set/reset
3114
+ * @throws phpmailerException
3115
+ * @return boolean
3116
+ * @TODO Should this not be using __set() magic function?
3117
+ */
3118
+ public function set($name, $value = '')
3119
+ {
3120
+ try {
3121
+ if (isset($this->$name)) {
3122
+ $this->$name = $value;
3123
+ } else {
3124
+ throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
3125
+ }
3126
+ } catch (Exception $exc) {
3127
+ $this->setError($exc->getMessage());
3128
+ if ($exc->getCode() == self::STOP_CRITICAL) {
3129
+ return false;
3130
+ }
3131
+ }
3132
+ return true;
3133
+ }
3134
+
3135
+ /**
3136
+ * Strip newlines to prevent header injection.
3137
+ * @access public
3138
+ * @param string $str
3139
+ * @return string
3140
+ */
3141
+ public function secureHeader($str)
3142
+ {
3143
+ return trim(str_replace(array("\r", "\n"), '', $str));
3144
+ }
3145
+
3146
+ /**
3147
+ * Normalize line breaks in a string.
3148
+ * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3149
+ * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3150
+ * @param string $text
3151
+ * @param string $breaktype What kind of line break to use, defaults to CRLF
3152
+ * @return string
3153
+ * @access public
3154
+ * @static
3155
+ */
3156
+ public static function normalizeBreaks($text, $breaktype = "\r\n")
3157
+ {
3158
+ return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3159
+ }
3160
+
3161
+
3162
+ /**
3163
+ * Set the public and private key files and password for S/MIME signing.
3164
+ * @access public
3165
+ * @param string $cert_filename
3166
+ * @param string $key_filename
3167
+ * @param string $key_pass Password for private key
3168
+ */
3169
+ public function sign($cert_filename, $key_filename, $key_pass)
3170
+ {
3171
+ $this->sign_cert_file = $cert_filename;
3172
+ $this->sign_key_file = $key_filename;
3173
+ $this->sign_key_pass = $key_pass;
3174
+ }
3175
+
3176
+ /**
3177
+ * Quoted-Printable-encode a DKIM header.
3178
+ * @access public
3179
+ * @param string $txt
3180
+ * @return string
3181
+ */
3182
+ public function DKIM_QP($txt)
3183
+ {
3184
+ $line = '';
3185
+ for ($i = 0; $i < strlen($txt); $i++) {
3186
+ $ord = ord($txt[$i]);
3187
+ if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3188
+ $line .= $txt[$i];
3189
+ } else {
3190
+ $line .= '=' . sprintf('%02X', $ord);
3191
+ }
3192
+ }
3193
+ return $line;
3194
+ }
3195
+
3196
+ /**
3197
+ * Generate a DKIM signature.
3198
+ * @access public
3199
+ * @param string $signHeader
3200
+ * @throws phpmailerException
3201
+ * @return string
3202
+ */
3203
+ public function DKIM_Sign($signHeader)
3204
+ {
3205
+ if (!defined('PKCS7_TEXT')) {
3206
+ if ($this->exceptions) {
3207
+ throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
3208
+ }
3209
+ return '';
3210
+ }
3211
+ $privKeyStr = file_get_contents($this->DKIM_private);
3212
+ if ($this->DKIM_passphrase != '') {
3213
+ $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3214
+ } else {
3215
+ $privKey = $privKeyStr;
3216
+ }
3217
+ if (openssl_sign($signHeader, $signature, $privKey)) {
3218
+ return base64_encode($signature);
3219
+ }
3220
+ return '';
3221
+ }
3222
+
3223
+ /**
3224
+ * Generate a DKIM canonicalization header.
3225
+ * @access public
3226
+ * @param string $signHeader Header
3227
+ * @return string
3228
+ */
3229
+ public function DKIM_HeaderC($signHeader)
3230
+ {
3231
+ $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3232
+ $lines = explode("\r\n", $signHeader);
3233
+ foreach ($lines as $key => $line) {
3234
+ list($heading, $value) = explode(':', $line, 2);
3235
+ $heading = strtolower($heading);
3236
+ $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
3237
+ $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3238
+ }
3239
+ $signHeader = implode("\r\n", $lines);
3240
+ return $signHeader;
3241
+ }
3242
+
3243
+ /**
3244
+ * Generate a DKIM canonicalization body.
3245
+ * @access public
3246
+ * @param string $body Message Body
3247
+ * @return string
3248
+ */
3249
+ public function DKIM_BodyC($body)
3250
+ {
3251
+ if ($body == '') {
3252
+ return "\r\n";
3253
+ }
3254
+ // stabilize line endings
3255
+ $body = str_replace("\r\n", "\n", $body);
3256
+ $body = str_replace("\n", "\r\n", $body);
3257
+ // END stabilize line endings
3258
+ while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3259
+ $body = substr($body, 0, strlen($body) - 2);
3260
+ }
3261
+ return $body;
3262
+ }
3263
+
3264
+ /**
3265
+ * Create the DKIM header and body in a new message header.
3266
+ * @access public
3267
+ * @param string $headers_line Header lines
3268
+ * @param string $subject Subject
3269
+ * @param string $body Body
3270
+ * @return string
3271
+ */
3272
+ public function DKIM_Add($headers_line, $subject, $body)
3273
+ {
3274
+ $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
3275
+ $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3276
+ $DKIMquery = 'dns/txt'; // Query method
3277
+ $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3278
+ $subject_header = "Subject: $subject";
3279
+ $headers = explode($this->LE, $headers_line);
3280
+ $from_header = '';
3281
+ $to_header = '';
3282
+ $current = '';
3283
+ foreach ($headers as $header) {
3284
+ if (strpos($header, 'From:') === 0) {
3285
+ $from_header = $header;
3286
+ $current = 'from_header';
3287
+ } elseif (strpos($header, 'To:') === 0) {
3288
+ $to_header = $header;
3289
+ $current = 'to_header';
3290
+ } else {
3291
+ if ($current && strpos($header, ' =?') === 0) {
3292
+ $current .= $header;
3293
+ } else {
3294
+ $current = '';
3295
+ }
3296
+ }
3297
+ }
3298
+ $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3299
+ $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3300
+ $subject = str_replace(
3301
+ '|',
3302
+ '=7C',
3303
+ $this->DKIM_QP($subject_header)
3304
+ ); // Copied header fields (dkim-quoted-printable)
3305
+ $body = $this->DKIM_BodyC($body);
3306
+ $DKIMlen = strlen($body); // Length of body
3307
+ $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
3308
+ $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';
3309
+ $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3310
+ $DKIMsignatureType . '; q=' .
3311
+ $DKIMquery . '; l=' .
3312
+ $DKIMlen . '; s=' .
3313
+ $this->DKIM_selector .
3314
+ ";\r\n" .
3315
+ "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3316
+ "\th=From:To:Subject;\r\n" .
3317
+ "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3318
+ "\tz=$from\r\n" .
3319
+ "\t|$to\r\n" .
3320
+ "\t|$subject;\r\n" .
3321
+ "\tbh=" . $DKIMb64 . ";\r\n" .
3322
+ "\tb=";
3323
+ $toSign = $this->DKIM_HeaderC(
3324
+ $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
3325
+ );
3326
+ $signed = $this->DKIM_Sign($toSign);
3327
+ return $dkimhdrs . $signed . "\r\n";
3328
+ }
3329
+
3330
+ /**
3331
+ * Allows for public read access to 'to' property.
3332
+ * @access public
3333
+ * @return array
3334
+ */
3335
+ public function getToAddresses()
3336
+ {
3337
+ return $this->to;
3338
+ }
3339
+
3340
+ /**
3341
+ * Allows for public read access to 'cc' property.
3342
+ * @access public
3343
+ * @return array
3344
+ */
3345
+ public function getCcAddresses()
3346
+ {
3347
+ return $this->cc;
3348
+ }
3349
+
3350
+ /**
3351
+ * Allows for public read access to 'bcc' property.
3352
+ * @access public
3353
+ * @return array
3354
+ */
3355
+ public function getBccAddresses()
3356
+ {
3357
+ return $this->bcc;
3358
+ }
3359
+
3360
+ /**
3361
+ * Allows for public read access to 'ReplyTo' property.
3362
+ * @access public
3363
+ * @return array
3364
+ */
3365
+ public function getReplyToAddresses()
3366
+ {
3367
+ return $this->ReplyTo;
3368
+ }
3369
+
3370
+ /**
3371
+ * Allows for public read access to 'all_recipients' property.
3372
+ * @access public
3373
+ * @return array
3374
+ */
3375
+ public function getAllRecipientAddresses()
3376
+ {
3377
+ return $this->all_recipients;
3378
+ }
3379
+
3380
+ /**
3381
+ * Perform a callback.
3382
+ * @param boolean $isSent
3383
+ * @param array $to
3384
+ * @param array $cc
3385
+ * @param array $bcc
3386
+ * @param string $subject
3387
+ * @param string $body
3388
+ * @param string $from
3389
+ */
3390
+ protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3391
+ {
3392
+ if (!empty($this->action_function) && is_callable($this->action_function)) {
3393
+ $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3394
+ call_user_func_array($this->action_function, $params);
3395
+ }
3396
+ }
3397
+ }
3398
+
3399
+ /**
3400
+ * PHPMailer exception handler
3401
+ * @package PHPMailer
3402
+ */
3403
+ class phpmailerException extends Exception
3404
+ {
3405
+ /**
3406
+ * Prettify error message output
3407
+ * @return string
3408
+ */
3409
+ public function errorMessage()
3410
+ {
3411
+ $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3412
+ return $errorMsg;
3413
+ }
3414
+ }
js/pack/class.smtp.php ADDED
@@ -0,0 +1,941 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PHPMailer RFC821 SMTP email transport class.
4
+ * PHP Version 5
5
+ * @package PHPMailer
6
+ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7
+ * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
8
+ * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9
+ * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10
+ * @author Brent R. Matzelle (original founder)
11
+ * @copyright 2014 Marcus Bointon
12
+ * @copyright 2010 - 2012 Jim Jagielski
13
+ * @copyright 2004 - 2009 Andy Prevost
14
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
16
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
+ * FITNESS FOR A PARTICULAR PURPOSE.
18
+ */
19
+
20
+ /**
21
+ * PHPMailer RFC821 SMTP email transport class.
22
+ * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
23
+ * @package PHPMailer
24
+ * @author Chris Ryan <unknown@example.com>
25
+ * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
26
+ */
27
+ class SMTP
28
+ {
29
+ /**
30
+ * The PHPMailer SMTP version number.
31
+ * @type string
32
+ */
33
+ const VERSION = '5.2.8';
34
+
35
+ /**
36
+ * SMTP line break constant.
37
+ * @type string
38
+ */
39
+ const CRLF = "\r\n";
40
+
41
+ /**
42
+ * The SMTP port to use if one is not specified.
43
+ * @type integer
44
+ */
45
+ const DEFAULT_SMTP_PORT = 25;
46
+
47
+ /**
48
+ * The maximum line length allowed by RFC 2822 section 2.1.1
49
+ * @type integer
50
+ */
51
+ const MAX_LINE_LENGTH = 998;
52
+
53
+ /**
54
+ * The PHPMailer SMTP Version number.
55
+ * @type string
56
+ * @deprecated Use the constant instead
57
+ * @see SMTP::VERSION
58
+ */
59
+ public $Version = '5.2.8';
60
+
61
+ /**
62
+ * SMTP server port number.
63
+ * @type integer
64
+ * @deprecated This is only ever used as a default value, so use the constant instead
65
+ * @see SMTP::DEFAULT_SMTP_PORT
66
+ */
67
+ public $SMTP_PORT = 25;
68
+
69
+ /**
70
+ * SMTP reply line ending.
71
+ * @type string
72
+ * @deprecated Use the constant instead
73
+ * @see SMTP::CRLF
74
+ */
75
+ public $CRLF = "\r\n";
76
+
77
+ /**
78
+ * Debug output level.
79
+ * Options:
80
+ * * `0` No output
81
+ * * `1` Commands
82
+ * * `2` Data and commands
83
+ * * `3` As 2 plus connection status
84
+ * * `4` Low-level data output
85
+ * @type integer
86
+ */
87
+ public $do_debug = 0;
88
+
89
+ /**
90
+ * How to handle debug output.
91
+ * Options:
92
+ * * `echo` Output plain-text as-is, appropriate for CLI
93
+ * * `html` Output escaped, line breaks converted to <br>, appropriate for browser output
94
+ * * `error_log` Output to error log as configured in php.ini
95
+ * @type string
96
+ */
97
+ public $Debugoutput = 'echo';
98
+
99
+ /**
100
+ * Whether to use VERP.
101
+ * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
102
+ * @link http://www.postfix.org/VERP_README.html Info on VERP
103
+ * @type boolean
104
+ */
105
+ public $do_verp = false;
106
+
107
+ /**
108
+ * The timeout value for connection, in seconds.
109
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
110
+ * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
111
+ * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
112
+ * @type integer
113
+ */
114
+ public $Timeout = 300;
115
+
116
+ /**
117
+ * The SMTP timelimit value for reads, in seconds.
118
+ * @type integer
119
+ */
120
+ public $Timelimit = 30;
121
+
122
+ /**
123
+ * The socket for the server connection.
124
+ * @type resource
125
+ */
126
+ protected $smtp_conn;
127
+
128
+ /**
129
+ * Error message, if any, for the last call.
130
+ * @type array
131
+ */
132
+ protected $error = array();
133
+
134
+ /**
135
+ * The reply the server sent to us for HELO.
136
+ * If null, no HELO string has yet been received.
137
+ * @type string|null
138
+ */
139
+ protected $helo_rply = null;
140
+
141
+ /**
142
+ * The most recent reply received from the server.
143
+ * @type string
144
+ */
145
+ protected $last_reply = '';
146
+
147
+ /**
148
+ * Output debugging info via a user-selected method.
149
+ * @param string $str Debug string to output
150
+ * @return void
151
+ */
152
+ protected function edebug($str)
153
+ {
154
+ switch ($this->Debugoutput) {
155
+ case 'error_log':
156
+ //Don't output, just log
157
+ error_log($str);
158
+ break;
159
+ case 'html':
160
+ //Cleans up output a bit for a better looking, HTML-safe output
161
+ echo htmlentities(
162
+ preg_replace('/[\r\n]+/', '', $str),
163
+ ENT_QUOTES,
164
+ 'UTF-8'
165
+ )
166
+ . "<br>\n";
167
+ break;
168
+ case 'echo':
169
+ default:
170
+ echo gmdate('Y-m-d H:i:s')."\t".trim($str)."\n";
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Connect to an SMTP server.
176
+ * @param string $host SMTP server IP or host name
177
+ * @param integer $port The port number to connect to
178
+ * @param integer $timeout How long to wait for the connection to open
179
+ * @param array $options An array of options for stream_context_create()
180
+ * @access public
181
+ * @return boolean
182
+ */
183
+ public function connect($host, $port = null, $timeout = 30, $options = array())
184
+ {
185
+ static $streamok;
186
+ //This is enabled by default since 5.0.0 but some providers disable it
187
+ //Check this once and cache the result
188
+ if (is_null($streamok)) {
189
+ $streamok = function_exists('stream_socket_client');
190
+ }
191
+ // Clear errors to avoid confusion
192
+ $this->error = array();
193
+ // Make sure we are __not__ connected
194
+ if ($this->connected()) {
195
+ // Already connected, generate error
196
+ $this->error = array('error' => 'Already connected to a server');
197
+ return false;
198
+ }
199
+ if (empty($port)) {
200
+ $port = self::DEFAULT_SMTP_PORT;
201
+ }
202
+ // Connect to the SMTP server
203
+ if ($this->do_debug >= 3) {
204
+ $this->edebug("Connection: opening to $host:$port, t=$timeout, opt=".var_export($options, true));
205
+ }
206
+ $errno = 0;
207
+ $errstr = '';
208
+ if ($streamok) {
209
+ $socket_context = stream_context_create($options);
210
+ //Suppress errors; connection failures are handled at a higher level
211
+ $this->smtp_conn = @stream_socket_client(
212
+ $host . ":" . $port,
213
+ $errno,
214
+ $errstr,
215
+ $timeout,
216
+ STREAM_CLIENT_CONNECT,
217
+ $socket_context
218
+ );
219
+ } else {
220
+ //Fall back to fsockopen which should work in more places, but is missing some features
221
+ if ($this->do_debug >= 3) {
222
+ $this->edebug("Connection: stream_socket_client not available, falling back to fsockopen");
223
+ }
224
+ $this->smtp_conn = fsockopen(
225
+ $host,
226
+ $port,
227
+ $errno,
228
+ $errstr,
229
+ $timeout
230
+ );
231
+ }
232
+ // Verify we connected properly
233
+ if (!is_resource($this->smtp_conn)) {
234
+ $this->error = array(
235
+ 'error' => 'Failed to connect to server',
236
+ 'errno' => $errno,
237
+ 'errstr' => $errstr
238
+ );
239
+ if ($this->do_debug >= 1) {
240
+ $this->edebug(
241
+ 'SMTP ERROR: ' . $this->error['error']
242
+ . ": $errstr ($errno)"
243
+ );
244
+ }
245
+ return false;
246
+ }
247
+ if ($this->do_debug >= 3) {
248
+ $this->edebug('Connection: opened');
249
+ }
250
+ // SMTP server can take longer to respond, give longer timeout for first read
251
+ // Windows does not have support for this timeout function
252
+ if (substr(PHP_OS, 0, 3) != 'WIN') {
253
+ $max = ini_get('max_execution_time');
254
+ if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
255
+ @set_time_limit($timeout);
256
+ }
257
+ stream_set_timeout($this->smtp_conn, $timeout, 0);
258
+ }
259
+ // Get any announcement
260
+ $announce = $this->get_lines();
261
+ if ($this->do_debug >= 2) {
262
+ $this->edebug('SERVER -> CLIENT: ' . $announce);
263
+ }
264
+ return true;
265
+ }
266
+
267
+ /**
268
+ * Initiate a TLS (encrypted) session.
269
+ * @access public
270
+ * @return boolean
271
+ */
272
+ public function startTLS()
273
+ {
274
+ if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
275
+ return false;
276
+ }
277
+ // Begin encrypted connection
278
+ if (!stream_socket_enable_crypto(
279
+ $this->smtp_conn,
280
+ true,
281
+ STREAM_CRYPTO_METHOD_TLS_CLIENT
282
+ )) {
283
+ return false;
284
+ }
285
+ return true;
286
+ }
287
+
288
+ /**
289
+ * Perform SMTP authentication.
290
+ * Must be run after hello().
291
+ * @see hello()
292
+ * @param string $username The user name
293
+ * @param string $password The password
294
+ * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
295
+ * @param string $realm The auth realm for NTLM
296
+ * @param string $workstation The auth workstation for NTLM
297
+ * @access public
298
+ * @return boolean True if successfully authenticated.
299
+ */
300
+ public function authenticate(
301
+ $username,
302
+ $password,
303
+ $authtype = 'LOGIN',
304
+ $realm = '',
305
+ $workstation = ''
306
+ ) {
307
+ if (empty($authtype)) {
308
+ $authtype = 'LOGIN';
309
+ }
310
+ switch ($authtype) {
311
+ case 'PLAIN':
312
+ // Start authentication
313
+ if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
314
+ return false;
315
+ }
316
+ // Send encoded username and password
317
+ if (!$this->sendCommand(
318
+ 'User & Password',
319
+ base64_encode("\0" . $username . "\0" . $password),
320
+ 235
321
+ )
322
+ ) {
323
+ return false;
324
+ }
325
+ break;
326
+ case 'LOGIN':
327
+ // Start authentication
328
+ if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
329
+ return false;
330
+ }
331
+ if (!$this->sendCommand("Username", base64_encode($username), 334)) {
332
+ return false;
333
+ }
334
+ if (!$this->sendCommand("Password", base64_encode($password), 235)) {
335
+ return false;
336
+ }
337
+ break;
338
+ case 'NTLM':
339
+ /*
340
+ * ntlm_sasl_client.php
341
+ * Bundled with Permission
342
+ *
343
+ * How to telnet in windows:
344
+ * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
345
+ * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
346
+ */
347
+ require_once 'extras/ntlm_sasl_client.php';
348
+ $temp = new stdClass();
349
+ $ntlm_client = new ntlm_sasl_client_class;
350
+ //Check that functions are available
351
+ if (!$ntlm_client->Initialize($temp)) {
352
+ $this->error = array('error' => $temp->error);
353
+ if ($this->do_debug >= 1) {
354
+ $this->edebug(
355
+ 'You need to enable some modules in your php.ini file: '
356
+ . $this->error['error']
357
+ );
358
+ }
359
+ return false;
360
+ }
361
+ //msg1
362
+ $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
363
+
364
+ if (!$this->sendCommand(
365
+ 'AUTH NTLM',
366
+ 'AUTH NTLM ' . base64_encode($msg1),
367
+ 334
368
+ )
369
+ ) {
370
+ return false;
371
+ }
372
+ //Though 0 based, there is a white space after the 3 digit number
373
+ //msg2
374
+ $challenge = substr($this->last_reply, 3);
375
+ $challenge = base64_decode($challenge);
376
+ $ntlm_res = $ntlm_client->NTLMResponse(
377
+ substr($challenge, 24, 8),
378
+ $password
379
+ );
380
+ //msg3
381
+ $msg3 = $ntlm_client->TypeMsg3(
382
+ $ntlm_res,
383
+ $username,
384
+ $realm,
385
+ $workstation
386
+ );
387
+ // send encoded username
388
+ return $this->sendCommand('Username', base64_encode($msg3), 235);
389
+ case 'CRAM-MD5':
390
+ // Start authentication
391
+ if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
392
+ return false;
393
+ }
394
+ // Get the challenge
395
+ $challenge = base64_decode(substr($this->last_reply, 4));
396
+
397
+ // Build the response
398
+ $response = $username . ' ' . $this->hmac($challenge, $password);
399
+
400
+ // send encoded credentials
401
+ return $this->sendCommand('Username', base64_encode($response), 235);
402
+ }
403
+ return true;
404
+ }
405
+
406
+ /**
407
+ * Calculate an MD5 HMAC hash.
408
+ * Works like hash_hmac('md5', $data, $key)
409
+ * in case that function is not available
410
+ * @param string $data The data to hash
411
+ * @param string $key The key to hash with
412
+ * @access protected
413
+ * @return string
414
+ */
415
+ protected function hmac($data, $key)
416
+ {
417
+ if (function_exists('hash_hmac')) {
418
+ return hash_hmac('md5', $data, $key);
419
+ }
420
+
421
+ // The following borrowed from
422
+ // http://php.net/manual/en/function.mhash.php#27225
423
+
424
+ // RFC 2104 HMAC implementation for php.
425
+ // Creates an md5 HMAC.
426
+ // Eliminates the need to install mhash to compute a HMAC
427
+ // Hacked by Lance Rushing
428
+
429
+ $bytelen = 64; // byte length for md5
430
+ if (strlen($key) > $bytelen) {
431
+ $key = pack('H*', md5($key));
432
+ }
433
+ $key = str_pad($key, $bytelen, chr(0x00));
434
+ $ipad = str_pad('', $bytelen, chr(0x36));
435
+ $opad = str_pad('', $bytelen, chr(0x5c));
436
+ $k_ipad = $key ^ $ipad;
437
+ $k_opad = $key ^ $opad;
438
+
439
+ return md5($k_opad . pack('H*', md5($k_ipad . $data)));
440
+ }
441
+
442
+ /**
443
+ * Check connection state.
444
+ * @access public
445
+ * @return boolean True if connected.
446
+ */
447
+ public function connected()
448
+ {
449
+ if (is_resource($this->smtp_conn)) {
450
+ $sock_status = stream_get_meta_data($this->smtp_conn);
451
+ if ($sock_status['eof']) {
452
+ // the socket is valid but we are not connected
453
+ if ($this->do_debug >= 1) {
454
+ $this->edebug(
455
+ 'SMTP NOTICE: EOF caught while checking if connected'
456
+ );
457
+ }
458
+ $this->close();
459
+ return false;
460
+ }
461
+ return true; // everything looks good
462
+ }
463
+ return false;
464
+ }
465
+
466
+ /**
467
+ * Close the socket and clean up the state of the class.
468
+ * Don't use this function without first trying to use QUIT.
469
+ * @see quit()
470
+ * @access public
471
+ * @return void
472
+ */
473
+ public function close()
474
+ {
475
+ $this->error = array();
476
+ $this->helo_rply = null;
477
+ if (is_resource($this->smtp_conn)) {
478
+ // close the connection and cleanup
479
+ fclose($this->smtp_conn);
480
+ if ($this->do_debug >= 3) {
481
+ $this->edebug('Connection: closed');
482
+ }
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Send an SMTP DATA command.
488
+ * Issues a data command and sends the msg_data to the server,
489
+ * finializing the mail transaction. $msg_data is the message
490
+ * that is to be send with the headers. Each header needs to be
491
+ * on a single line followed by a <CRLF> with the message headers
492
+ * and the message body being separated by and additional <CRLF>.
493
+ * Implements rfc 821: DATA <CRLF>
494
+ * @param string $msg_data Message data to send
495
+ * @access public
496
+ * @return boolean
497
+ */
498
+ public function data($msg_data)
499
+ {
500
+ if (!$this->sendCommand('DATA', 'DATA', 354)) {
501
+ return false;
502
+ }
503
+ /* The server is ready to accept data!
504
+ * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
505
+ * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
506
+ * smaller lines to fit within the limit.
507
+ * We will also look for lines that start with a '.' and prepend an additional '.'.
508
+ * NOTE: this does not count towards line-length limit.
509
+ */
510
+
511
+ // Normalize line breaks before exploding
512
+ $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
513
+
514
+ /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
515
+ * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
516
+ * process all lines before a blank line as headers.
517
+ */
518
+
519
+ $field = substr($lines[0], 0, strpos($lines[0], ':'));
520
+ $in_headers = false;
521
+ if (!empty($field) && strpos($field, ' ') === false) {
522
+ $in_headers = true;
523
+ }
524
+
525
+ foreach ($lines as $line) {
526
+ $lines_out = array();
527
+ if ($in_headers and $line == '') {
528
+ $in_headers = false;
529
+ }
530
+ // ok we need to break this line up into several smaller lines
531
+ //This is a small micro-optimisation: isset($str[$len]) is equivalent to (strlen($str) > $len)
532
+ while (isset($line[self::MAX_LINE_LENGTH])) {
533
+ //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
534
+ //so as to avoid breaking in the middle of a word
535
+ $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
536
+ if (!$pos) { //Deliberately matches both false and 0
537
+ //No nice break found, add a hard break
538
+ $pos = self::MAX_LINE_LENGTH - 1;
539
+ $lines_out[] = substr($line, 0, $pos);
540
+ $line = substr($line, $pos);
541
+ } else {
542
+ //Break at the found point
543
+ $lines_out[] = substr($line, 0, $pos);
544
+ //Move along by the amount we dealt with
545
+ $line = substr($line, $pos + 1);
546
+ }
547
+ /* If processing headers add a LWSP-char to the front of new line
548
+ * RFC822 section 3.1.1
549
+ */
550
+ if ($in_headers) {
551
+ $line = "\t" . $line;
552
+ }
553
+ }
554
+ $lines_out[] = $line;
555
+
556
+ // Send the lines to the server
557
+ foreach ($lines_out as $line_out) {
558
+ //RFC2821 section 4.5.2
559
+ if (!empty($line_out) and $line_out[0] == '.') {
560
+ $line_out = '.' . $line_out;
561
+ }
562
+ $this->client_send($line_out . self::CRLF);
563
+ }
564
+ }
565
+
566
+ // Message data has been sent, complete the command
567
+ return $this->sendCommand('DATA END', '.', 250);
568
+ }
569
+
570
+ /**
571
+ * Send an SMTP HELO or EHLO command.
572
+ * Used to identify the sending server to the receiving server.
573
+ * This makes sure that client and server are in a known state.
574
+ * Implements RFC 821: HELO <SP> <domain> <CRLF>
575
+ * and RFC 2821 EHLO.
576
+ * @param string $host The host name or IP to connect to
577
+ * @access public
578
+ * @return boolean
579
+ */
580
+ public function hello($host = '')
581
+ {
582
+ // Try extended hello first (RFC 2821)
583
+ return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
584
+ }
585
+
586
+ /**
587
+ * Send an SMTP HELO or EHLO command.
588
+ * Low-level implementation used by hello()
589
+ * @see hello()
590
+ * @param string $hello The HELO string
591
+ * @param string $host The hostname to say we are
592
+ * @access protected
593
+ * @return boolean
594
+ */
595
+ protected function sendHello($hello, $host)
596
+ {
597
+ $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
598
+ $this->helo_rply = $this->last_reply;
599
+ return $noerror;
600
+ }
601
+
602
+ /**
603
+ * Send an SMTP MAIL command.
604
+ * Starts a mail transaction from the email address specified in
605
+ * $from. Returns true if successful or false otherwise. If True
606
+ * the mail transaction is started and then one or more recipient
607
+ * commands may be called followed by a data command.
608
+ * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
609
+ * @param string $from Source address of this message
610
+ * @access public
611
+ * @return boolean
612
+ */
613
+ public function mail($from)
614
+ {
615
+ $useVerp = ($this->do_verp ? ' XVERP' : '');
616
+ return $this->sendCommand(
617
+ 'MAIL FROM',
618
+ 'MAIL FROM:<' . $from . '>' . $useVerp,
619
+ 250
620
+ );
621
+ }
622
+
623
+ /**
624
+ * Send an SMTP QUIT command.
625
+ * Closes the socket if there is no error or the $close_on_error argument is true.
626
+ * Implements from rfc 821: QUIT <CRLF>
627
+ * @param boolean $close_on_error Should the connection close if an error occurs?
628
+ * @access public
629
+ * @return boolean
630
+ */
631
+ public function quit($close_on_error = true)
632
+ {
633
+ $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
634
+ $err = $this->error; //Save any error
635
+ if ($noerror or $close_on_error) {
636
+ $this->close();
637
+ $this->error = $err; //Restore any error from the quit command
638
+ }
639
+ return $noerror;
640
+ }
641
+
642
+ /**
643
+ * Send an SMTP RCPT command.
644
+ * Sets the TO argument to $toaddr.
645
+ * Returns true if the recipient was accepted false if it was rejected.
646
+ * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
647
+ * @param string $toaddr The address the message is being sent to
648
+ * @access public
649
+ * @return boolean
650
+ */
651
+ public function recipient($toaddr)
652
+ {
653
+ return $this->sendCommand(
654
+ 'RCPT TO',
655
+ 'RCPT TO:<' . $toaddr . '>',
656
+ array(250, 251)
657
+ );
658
+ }
659
+
660
+ /**
661
+ * Send an SMTP RSET command.
662
+ * Abort any transaction that is currently in progress.
663
+ * Implements rfc 821: RSET <CRLF>
664
+ * @access public
665
+ * @return boolean True on success.
666
+ */
667
+ public function reset()
668
+ {
669
+ return $this->sendCommand('RSET', 'RSET', 250);
670
+ }
671
+
672
+ /**
673
+ * Send a command to an SMTP server and check its return code.
674
+ * @param string $command The command name - not sent to the server
675
+ * @param string $commandstring The actual command to send
676
+ * @param integer|array $expect One or more expected integer success codes
677
+ * @access protected
678
+ * @return boolean True on success.
679
+ */
680
+ protected function sendCommand($command, $commandstring, $expect)
681
+ {
682
+ if (!$this->connected()) {
683
+ $this->error = array(
684
+ 'error' => "Called $command without being connected"
685
+ );
686
+ return false;
687
+ }
688
+ $this->client_send($commandstring . self::CRLF);
689
+
690
+ $reply = $this->get_lines();
691
+ $code = substr($reply, 0, 3);
692
+
693
+ if ($this->do_debug >= 2) {
694
+ $this->edebug('SERVER -> CLIENT: ' . $reply);
695
+ }
696
+
697
+ if (!in_array($code, (array)$expect)) {
698
+ $this->last_reply = null;
699
+ $this->error = array(
700
+ 'error' => "$command command failed",
701
+ 'smtp_code' => $code,
702
+ 'detail' => substr($reply, 4)
703
+ );
704
+ if ($this->do_debug >= 1) {
705
+ $this->edebug(
706
+ 'SMTP ERROR: ' . $this->error['error'] . ': ' . $reply
707
+ );
708
+ }
709
+ return false;
710
+ }
711
+
712
+ $this->last_reply = $reply;
713
+ $this->error = array();
714
+ return true;
715
+ }
716
+
717
+ /**
718
+ * Send an SMTP SAML command.
719
+ * Starts a mail transaction from the email address specified in $from.
720
+ * Returns true if successful or false otherwise. If True
721
+ * the mail transaction is started and then one or more recipient
722
+ * commands may be called followed by a data command. This command
723
+ * will send the message to the users terminal if they are logged
724
+ * in and send them an email.
725
+ * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
726
+ * @param string $from The address the message is from
727
+ * @access public
728
+ * @return boolean
729
+ */
730
+ public function sendAndMail($from)
731
+ {
732
+ return $this->sendCommand('SAML', "SAML FROM:$from", 250);
733
+ }
734
+
735
+ /**
736
+ * Send an SMTP VRFY command.
737
+ * @param string $name The name to verify
738
+ * @access public
739
+ * @return boolean
740
+ */
741
+ public function verify($name)
742
+ {
743
+ return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
744
+ }
745
+
746
+ /**
747
+ * Send an SMTP NOOP command.
748
+ * Used to keep keep-alives alive, doesn't actually do anything
749
+ * @access public
750
+ * @return boolean
751
+ */
752
+ public function noop()
753
+ {
754
+ return $this->sendCommand('NOOP', 'NOOP', 250);
755
+ }
756
+
757
+ /**
758
+ * Send an SMTP TURN command.
759
+ * This is an optional command for SMTP that this class does not support.
760
+ * This method is here to make the RFC821 Definition complete for this class
761
+ * and _may_ be implemented in future
762
+ * Implements from rfc 821: TURN <CRLF>
763
+ * @access public
764
+ * @return boolean
765
+ */
766
+ public function turn()
767
+ {
768
+ $this->error = array(
769
+ 'error' => 'The SMTP TURN command is not implemented'
770
+ );
771
+ if ($this->do_debug >= 1) {
772
+ $this->edebug('SMTP NOTICE: ' . $this->error['error']);
773
+ }
774
+ return false;
775
+ }
776
+
777
+ /**
778
+ * Send raw data to the server.
779
+ * @param string $data The data to send
780
+ * @access public
781
+ * @return integer|boolean The number of bytes sent to the server or false on error
782
+ */
783
+ public function client_send($data)
784
+ {
785
+ if ($this->do_debug >= 1) {
786
+ $this->edebug("CLIENT -> SERVER: $data");
787
+ }
788
+ return fwrite($this->smtp_conn, $data);
789
+ }
790
+
791
+ /**
792
+ * Get the latest error.
793
+ * @access public
794
+ * @return array
795
+ */
796
+ public function getError()
797
+ {
798
+ return $this->error;
799
+ }
800
+
801
+ /**
802
+ * Get the last reply from the server.
803
+ * @access public
804
+ * @return string
805
+ */
806
+ public function getLastReply()
807
+ {
808
+ return $this->last_reply;
809
+ }
810
+
811
+ /**
812
+ * Read the SMTP server's response.
813
+ * Either before eof or socket timeout occurs on the operation.
814
+ * With SMTP we can tell if we have more lines to read if the
815
+ * 4th character is '-' symbol. If it is a space then we don't
816
+ * need to read anything else.
817
+ * @access protected
818
+ * @return string
819
+ */
820
+ protected function get_lines()
821
+ {
822
+ // If the connection is bad, give up straight away
823
+ if (!is_resource($this->smtp_conn)) {
824
+ return '';
825
+ }
826
+ $data = '';
827
+ $endtime = 0;
828
+ stream_set_timeout($this->smtp_conn, $this->Timeout);
829
+ if ($this->Timelimit > 0) {
830
+ $endtime = time() + $this->Timelimit;
831
+ }
832
+ while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
833
+ $str = @fgets($this->smtp_conn, 515);
834
+ if ($this->do_debug >= 4) {
835
+ $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
836
+ $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
837
+ }
838
+ $data .= $str;
839
+ if ($this->do_debug >= 4) {
840
+ $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
841
+ }
842
+ // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
843
+ if ((isset($str[3]) and $str[3] == ' ')) {
844
+ break;
845
+ }
846
+ // Timed-out? Log and break
847
+ $info = stream_get_meta_data($this->smtp_conn);
848
+ if ($info['timed_out']) {
849
+ if ($this->do_debug >= 4) {
850
+ $this->edebug(
851
+ 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)'
852
+ );
853
+ }
854
+ break;
855
+ }
856
+ // Now check if reads took too long
857
+ if ($endtime and time() > $endtime) {
858
+ if ($this->do_debug >= 4) {
859
+ $this->edebug(
860
+ 'SMTP -> get_lines(): timelimit reached ('.
861
+ $this->Timelimit . ' sec)'
862
+ );
863
+ }
864
+ break;
865
+ }
866
+ }
867
+ return $data;
868
+ }
869
+
870
+ /**
871
+ * Enable or disable VERP address generation.
872
+ * @param boolean $enabled
873
+ */
874
+ public function setVerp($enabled = false)
875
+ {
876
+ $this->do_verp = $enabled;
877
+ }
878
+
879
+ /**
880
+ * Get VERP address generation mode.
881
+ * @return boolean
882
+ */
883
+ public function getVerp()
884
+ {
885
+ return $this->do_verp;
886
+ }
887
+
888
+ /**
889
+ * Set debug output method.
890
+ * @param string $method The function/method to use for debugging output.
891
+ */
892
+ public function setDebugOutput($method = 'echo')
893
+ {
894
+ $this->Debugoutput = $method;
895
+ }
896
+
897
+ /**
898
+ * Get debug output method.
899
+ * @return string
900
+ */
901
+ public function getDebugOutput()
902
+ {
903
+ return $this->Debugoutput;
904
+ }
905
+
906
+ /**
907
+ * Set debug output level.
908
+ * @param integer $level
909
+ */
910
+ public function setDebugLevel($level = 0)
911
+ {
912
+ $this->do_debug = $level;
913
+ }
914
+
915
+ /**
916
+ * Get debug output level.
917
+ * @return integer
918
+ */
919
+ public function getDebugLevel()
920
+ {
921
+ return $this->do_debug;
922
+ }
923
+
924
+ /**
925
+ * Set SMTP timeout.
926
+ * @param integer $timeout
927
+ */
928
+ public function setTimeout($timeout = 0)
929
+ {
930
+ $this->Timeout = $timeout;
931
+ }
932
+
933
+ /**
934
+ * Get SMTP timeout.
935
+ * @return integer
936
+ */
937
+ public function getTimeout()
938
+ {
939
+ return $this->Timeout;
940
+ }
941
+ }
js/pack/jquery-1.10.2.js ADDED
@@ -0,0 +1,9789 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery JavaScript Library v1.10.2
3
+ * http://jquery.com/
4
+ *
5
+ * Includes Sizzle.js
6
+ * http://sizzlejs.com/
7
+ *
8
+ * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
9
+ * Released under the MIT license
10
+ * http://jquery.org/license
11
+ *
12
+ * Date: 2013-07-03T13:48Z
13
+ */
14
+ (function( window, undefined ) {
15
+
16
+ // Can't do this because several apps including ASP.NET trace
17
+ // the stack via arguments.caller.callee and Firefox dies if
18
+ // you try to trace through "use strict" call chains. (#13335)
19
+ // Support: Firefox 18+
20
+ //"use strict";
21
+ var
22
+ // The deferred used on DOM ready
23
+ readyList,
24
+
25
+ // A central reference to the root jQuery(document)
26
+ rootjQuery,
27
+
28
+ // Support: IE<10
29
+ // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined`
30
+ core_strundefined = typeof undefined,
31
+
32
+ // Use the correct document accordingly with window argument (sandbox)
33
+ location = window.location,
34
+ document = window.document,
35
+ docElem = document.documentElement,
36
+
37
+ // Map over jQuery in case of overwrite
38
+ _jQuery = window.jQuery,
39
+
40
+ // Map over the $ in case of overwrite
41
+ _$ = window.$,
42
+
43
+ // [[Class]] -> type pairs
44
+ class2type = {},
45
+
46
+ // List of deleted data cache ids, so we can reuse them
47
+ core_deletedIds = [],
48
+
49
+ core_version = "1.10.2",
50
+
51
+ // Save a reference to some core methods
52
+ core_concat = core_deletedIds.concat,
53
+ core_push = core_deletedIds.push,
54
+ core_slice = core_deletedIds.slice,
55
+ core_indexOf = core_deletedIds.indexOf,
56
+ core_toString = class2type.toString,
57
+ core_hasOwn = class2type.hasOwnProperty,
58
+ core_trim = core_version.trim,
59
+
60
+ // Define a local copy of jQuery
61
+ jQuery = function( selector, context ) {
62
+ // The jQuery object is actually just the init constructor 'enhanced'
63
+ return new jQuery.fn.init( selector, context, rootjQuery );
64
+ },
65
+
66
+ // Used for matching numbers
67
+ core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
68
+
69
+ // Used for splitting on whitespace
70
+ core_rnotwhite = /\S+/g,
71
+
72
+ // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
73
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
74
+
75
+ // A simple way to check for HTML strings
76
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
77
+ // Strict HTML recognition (#11290: must start with <)
78
+ rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
79
+
80
+ // Match a standalone tag
81
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
82
+
83
+ // JSON RegExp
84
+ rvalidchars = /^[\],:{}\s]*$/,
85
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
86
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
87
+ rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
88
+
89
+ // Matches dashed string for camelizing
90
+ rmsPrefix = /^-ms-/,
91
+ rdashAlpha = /-([\da-z])/gi,
92
+
93
+ // Used by jQuery.camelCase as callback to replace()
94
+ fcamelCase = function( all, letter ) {
95
+ return letter.toUpperCase();
96
+ },
97
+
98
+ // The ready event handler
99
+ completed = function( event ) {
100
+
101
+ // readyState === "complete" is good enough for us to call the dom ready in oldIE
102
+ if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
103
+ detach();
104
+ jQuery.ready();
105
+ }
106
+ },
107
+ // Clean-up method for dom ready events
108
+ detach = function() {
109
+ if ( document.addEventListener ) {
110
+ document.removeEventListener( "DOMContentLoaded", completed, false );
111
+ window.removeEventListener( "load", completed, false );
112
+
113
+ } else {
114
+ document.detachEvent( "onreadystatechange", completed );
115
+ window.detachEvent( "onload", completed );
116
+ }
117
+ };
118
+
119
+ jQuery.fn = jQuery.prototype = {
120
+ // The current version of jQuery being used
121
+ jquery: core_version,
122
+
123
+ constructor: jQuery,
124
+ init: function( selector, context, rootjQuery ) {
125
+ var match, elem;
126
+
127
+ // HANDLE: $(""), $(null), $(undefined), $(false)
128
+ if ( !selector ) {
129
+ return this;
130
+ }
131
+
132
+ // Handle HTML strings
133
+ if ( typeof selector === "string" ) {
134
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
135
+ // Assume that strings that start and end with <> are HTML and skip the regex check
136
+ match = [ null, selector, null ];
137
+
138
+ } else {
139
+ match = rquickExpr.exec( selector );
140
+ }
141
+
142
+ // Match html or make sure no context is specified for #id
143
+ if ( match && (match[1] || !context) ) {
144
+
145
+ // HANDLE: $(html) -> $(array)
146
+ if ( match[1] ) {
147
+ context = context instanceof jQuery ? context[0] : context;
148
+
149
+ // scripts is true for back-compat
150
+ jQuery.merge( this, jQuery.parseHTML(
151
+ match[1],
152
+ context && context.nodeType ? context.ownerDocument || context : document,
153
+ true
154
+ ) );
155
+
156
+ // HANDLE: $(html, props)
157
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
158
+ for ( match in context ) {
159
+ // Properties of context are called as methods if possible
160
+ if ( jQuery.isFunction( this[ match ] ) ) {
161
+ this[ match ]( context[ match ] );
162
+
163
+ // ...and otherwise set as attributes
164
+ } else {
165
+ this.attr( match, context[ match ] );
166
+ }
167
+ }
168
+ }
169
+
170
+ return this;
171
+
172
+ // HANDLE: $(#id)
173
+ } else {
174
+ elem = document.getElementById( match[2] );
175
+
176
+ // Check parentNode to catch when Blackberry 4.6 returns
177
+ // nodes that are no longer in the document #6963
178
+ if ( elem && elem.parentNode ) {
179
+ // Handle the case where IE and Opera return items
180
+ // by name instead of ID
181
+ if ( elem.id !== match[2] ) {
182
+ return rootjQuery.find( selector );
183
+ }
184
+
185
+ // Otherwise, we inject the element directly into the jQuery object
186
+ this.length = 1;
187
+ this[0] = elem;
188
+ }
189
+
190
+ this.context = document;
191
+ this.selector = selector;
192
+ return this;
193
+ }
194
+
195
+ // HANDLE: $(expr, $(...))
196
+ } else if ( !context || context.jquery ) {
197
+ return ( context || rootjQuery ).find( selector );
198
+
199
+ // HANDLE: $(expr, context)
200
+ // (which is just equivalent to: $(context).find(expr)
201
+ } else {
202
+ return this.constructor( context ).find( selector );
203
+ }
204
+
205
+ // HANDLE: $(DOMElement)
206
+ } else if ( selector.nodeType ) {
207
+ this.context = this[0] = selector;
208
+ this.length = 1;
209
+ return this;
210
+
211
+ // HANDLE: $(function)
212
+ // Shortcut for document ready
213
+ } else if ( jQuery.isFunction( selector ) ) {
214
+ return rootjQuery.ready( selector );
215
+ }
216
+
217
+ if ( selector.selector !== undefined ) {
218
+ this.selector = selector.selector;
219
+ this.context = selector.context;
220
+ }
221
+
222
+ return jQuery.makeArray( selector, this );
223
+ },
224
+
225
+ // Start with an empty selector
226
+ selector: "",
227
+
228
+ // The default length of a jQuery object is 0
229
+ length: 0,
230
+
231
+ toArray: function() {
232
+ return core_slice.call( this );
233
+ },
234
+
235
+ // Get the Nth element in the matched element set OR
236
+ // Get the whole matched element set as a clean array
237
+ get: function( num ) {
238
+ return num == null ?
239
+
240
+ // Return a 'clean' array
241
+ this.toArray() :
242
+
243
+ // Return just the object
244
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
245
+ },
246
+
247
+ // Take an array of elements and push it onto the stack
248
+ // (returning the new matched element set)
249
+ pushStack: function( elems ) {
250
+
251
+ // Build a new jQuery matched element set
252
+ var ret = jQuery.merge( this.constructor(), elems );
253
+
254
+ // Add the old object onto the stack (as a reference)
255
+ ret.prevObject = this;
256
+ ret.context = this.context;
257
+
258
+ // Return the newly-formed element set
259
+ return ret;
260
+ },
261
+
262
+ // Execute a callback for every element in the matched set.
263
+ // (You can seed the arguments with an array of args, but this is
264
+ // only used internally.)
265
+ each: function( callback, args ) {
266
+ return jQuery.each( this, callback, args );
267
+ },
268
+
269
+ ready: function( fn ) {
270
+ // Add the callback
271
+ jQuery.ready.promise().done( fn );
272
+
273
+ return this;
274
+ },
275
+
276
+ slice: function() {
277
+ return this.pushStack( core_slice.apply( this, arguments ) );
278
+ },
279
+
280
+ first: function() {
281
+ return this.eq( 0 );
282
+ },
283
+
284
+ last: function() {
285
+ return this.eq( -1 );
286
+ },
287
+
288
+ eq: function( i ) {
289
+ var len = this.length,
290
+ j = +i + ( i < 0 ? len : 0 );
291
+ return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
292
+ },
293
+
294
+ map: function( callback ) {
295
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
296
+ return callback.call( elem, i, elem );
297
+ }));
298
+ },
299
+
300
+ end: function() {
301
+ return this.prevObject || this.constructor(null);
302
+ },
303
+
304
+ // For internal use only.
305
+ // Behaves like an Array's method, not like a jQuery method.
306
+ push: core_push,
307
+ sort: [].sort,
308
+ splice: [].splice
309
+ };
310
+
311
+ // Give the init function the jQuery prototype for later instantiation
312
+ jQuery.fn.init.prototype = jQuery.fn;
313
+
314
+ jQuery.extend = jQuery.fn.extend = function() {
315
+ var src, copyIsArray, copy, name, options, clone,
316
+ target = arguments[0] || {},
317
+ i = 1,
318
+ length = arguments.length,
319
+ deep = false;
320
+
321
+ // Handle a deep copy situation
322
+ if ( typeof target === "boolean" ) {
323
+ deep = target;
324
+ target = arguments[1] || {};
325
+ // skip the boolean and the target
326
+ i = 2;
327
+ }
328
+
329
+ // Handle case when target is a string or something (possible in deep copy)
330
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
331
+ target = {};
332
+ }
333
+
334
+ // extend jQuery itself if only one argument is passed
335
+ if ( length === i ) {
336
+ target = this;
337
+ --i;
338
+ }
339
+
340
+ for ( ; i < length; i++ ) {
341
+ // Only deal with non-null/undefined values
342
+ if ( (options = arguments[ i ]) != null ) {
343
+ // Extend the base object
344
+ for ( name in options ) {
345
+ src = target[ name ];
346
+ copy = options[ name ];
347
+
348
+ // Prevent never-ending loop
349
+ if ( target === copy ) {
350
+ continue;
351
+ }
352
+
353
+ // Recurse if we're merging plain objects or arrays
354
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
355
+ if ( copyIsArray ) {
356
+ copyIsArray = false;
357
+ clone = src && jQuery.isArray(src) ? src : [];
358
+
359
+ } else {
360
+ clone = src && jQuery.isPlainObject(src) ? src : {};
361
+ }
362
+
363
+ // Never move original objects, clone them
364
+ target[ name ] = jQuery.extend( deep, clone, copy );
365
+
366
+ // Don't bring in undefined values
367
+ } else if ( copy !== undefined ) {
368
+ target[ name ] = copy;
369
+ }
370
+ }
371
+ }
372
+ }
373
+
374
+ // Return the modified object
375
+ return target;
376
+ };
377
+
378
+ jQuery.extend({
379
+ // Unique for each copy of jQuery on the page
380
+ // Non-digits removed to match rinlinejQuery
381
+ expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
382
+
383
+ noConflict: function( deep ) {
384
+ if ( window.$ === jQuery ) {
385
+ window.$ = _$;
386
+ }
387
+
388
+ if ( deep && window.jQuery === jQuery ) {
389
+ window.jQuery = _jQuery;
390
+ }
391
+
392
+ return jQuery;
393
+ },
394
+
395
+ // Is the DOM ready to be used? Set to true once it occurs.
396
+ isReady: false,
397
+
398
+ // A counter to track how many items to wait for before
399
+ // the ready event fires. See #6781
400
+ readyWait: 1,
401
+
402
+ // Hold (or release) the ready event
403
+ holdReady: function( hold ) {
404
+ if ( hold ) {
405
+ jQuery.readyWait++;
406
+ } else {
407
+ jQuery.ready( true );
408
+ }
409
+ },
410
+
411
+ // Handle when the DOM is ready
412
+ ready: function( wait ) {
413
+
414
+ // Abort if there are pending holds or we're already ready
415
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
416
+ return;
417
+ }
418
+
419
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
420
+ if ( !document.body ) {
421
+ return setTimeout( jQuery.ready );
422
+ }
423
+
424
+ // Remember that the DOM is ready
425
+ jQuery.isReady = true;
426
+
427
+ // If a normal DOM Ready event fired, decrement, and wait if need be
428
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
429
+ return;
430
+ }
431
+
432
+ // If there are functions bound, to execute
433
+ readyList.resolveWith( document, [ jQuery ] );
434
+
435
+ // Trigger any bound ready events
436
+ if ( jQuery.fn.trigger ) {
437
+ jQuery( document ).trigger("ready").off("ready");
438
+ }
439
+ },
440
+
441
+ // See test/unit/core.js for details concerning isFunction.
442
+ // Since version 1.3, DOM methods and functions like alert
443
+ // aren't supported. They return false on IE (#2968).
444
+ isFunction: function( obj ) {
445
+ return jQuery.type(obj) === "function";
446
+ },
447
+
448
+ isArray: Array.isArray || function( obj ) {
449
+ return jQuery.type(obj) === "array";
450
+ },
451
+
452
+ isWindow: function( obj ) {
453
+ /* jshint eqeqeq: false */
454
+ return obj != null && obj == obj.window;
455
+ },
456
+
457
+ isNumeric: function( obj ) {
458
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
459
+ },
460
+
461
+ type: function( obj ) {
462
+ if ( obj == null ) {
463
+ return String( obj );
464
+ }
465
+ return typeof obj === "object" || typeof obj === "function" ?
466
+ class2type[ core_toString.call(obj) ] || "object" :
467
+ typeof obj;
468
+ },
469
+
470
+ isPlainObject: function( obj ) {
471
+ var key;
472
+
473
+ // Must be an Object.
474
+ // Because of IE, we also have to check the presence of the constructor property.
475
+ // Make sure that DOM nodes and window objects don't pass through, as well
476
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
477
+ return false;
478
+ }
479
+
480
+ try {
481
+ // Not own constructor property must be Object
482
+ if ( obj.constructor &&
483
+ !core_hasOwn.call(obj, "constructor") &&
484
+ !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
485
+ return false;
486
+ }
487
+ } catch ( e ) {
488
+ // IE8,9 Will throw exceptions on certain host objects #9897
489
+ return false;
490
+ }
491
+
492
+ // Support: IE<9
493
+ // Handle iteration over inherited properties before own properties.
494
+ if ( jQuery.support.ownLast ) {
495
+ for ( key in obj ) {
496
+ return core_hasOwn.call( obj, key );
497
+ }
498
+ }
499
+
500
+ // Own properties are enumerated firstly, so to speed up,
501
+ // if last one is own, then all properties are own.
502
+ for ( key in obj ) {}
503
+
504
+ return key === undefined || core_hasOwn.call( obj, key );
505
+ },
506
+
507
+ isEmptyObject: function( obj ) {
508
+ var name;
509
+ for ( name in obj ) {
510
+ return false;
511
+ }
512
+ return true;
513
+ },
514
+
515
+ error: function( msg ) {
516
+ throw new Error( msg );
517
+ },
518
+
519
+ // data: string of html
520
+ // context (optional): If specified, the fragment will be created in this context, defaults to document
521
+ // keepScripts (optional): If true, will include scripts passed in the html string
522
+ parseHTML: function( data, context, keepScripts ) {
523
+ if ( !data || typeof data !== "string" ) {
524
+ return null;
525
+ }
526
+ if ( typeof context === "boolean" ) {
527
+ keepScripts = context;
528
+ context = false;
529
+ }
530
+ context = context || document;
531
+
532
+ var parsed = rsingleTag.exec( data ),
533
+ scripts = !keepScripts && [];
534
+
535
+ // Single tag
536
+ if ( parsed ) {
537
+ return [ context.createElement( parsed[1] ) ];
538
+ }
539
+
540
+ parsed = jQuery.buildFragment( [ data ], context, scripts );
541
+ if ( scripts ) {
542
+ jQuery( scripts ).remove();
543
+ }
544
+ return jQuery.merge( [], parsed.childNodes );
545
+ },
546
+
547
+ parseJSON: function( data ) {
548
+ // Attempt to parse using the native JSON parser first
549
+ if ( window.JSON && window.JSON.parse ) {
550
+ return window.JSON.parse( data );
551
+ }
552
+
553
+ if ( data === null ) {
554
+ return data;
555
+ }
556
+
557
+ if ( typeof data === "string" ) {
558
+
559
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
560
+ data = jQuery.trim( data );
561
+
562
+ if ( data ) {
563
+ // Make sure the incoming data is actual JSON
564
+ // Logic borrowed from http://json.org/json2.js
565
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
566
+ .replace( rvalidtokens, "]" )
567
+ .replace( rvalidbraces, "")) ) {
568
+
569
+ return ( new Function( "return " + data ) )();
570
+ }
571
+ }
572
+ }
573
+
574
+ jQuery.error( "Invalid JSON: " + data );
575
+ },
576
+
577
+ // Cross-browser xml parsing
578
+ parseXML: function( data ) {
579
+ var xml, tmp;
580
+ if ( !data || typeof data !== "string" ) {
581
+ return null;
582
+ }
583
+ try {
584
+ if ( window.DOMParser ) { // Standard
585
+ tmp = new DOMParser();
586
+ xml = tmp.parseFromString( data , "text/xml" );
587
+ } else { // IE
588
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
589
+ xml.async = "false";
590
+ xml.loadXML( data );
591
+ }
592
+ } catch( e ) {
593
+ xml = undefined;
594
+ }
595
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
596
+ jQuery.error( "Invalid XML: " + data );
597
+ }
598
+ return xml;
599
+ },
600
+
601
+ noop: function() {},
602
+
603
+ // Evaluates a script in a global context
604
+ // Workarounds based on findings by Jim Driscoll
605
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
606
+ globalEval: function( data ) {
607
+ if ( data && jQuery.trim( data ) ) {
608
+ // We use execScript on Internet Explorer
609
+ // We use an anonymous function so that context is window
610
+ // rather than jQuery in Firefox
611
+ ( window.execScript || function( data ) {
612
+ window[ "eval" ].call( window, data );
613
+ } )( data );
614
+ }
615
+ },
616
+
617
+ // Convert dashed to camelCase; used by the css and data modules
618
+ // Microsoft forgot to hump their vendor prefix (#9572)
619
+ camelCase: function( string ) {
620
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
621
+ },
622
+
623
+ nodeName: function( elem, name ) {
624
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
625
+ },
626
+
627
+ // args is for internal usage only
628
+ each: function( obj, callback, args ) {
629
+ var value,
630
+ i = 0,
631
+ length = obj.length,
632
+ isArray = isArraylike( obj );
633
+
634
+ if ( args ) {
635
+ if ( isArray ) {
636
+ for ( ; i < length; i++ ) {
637
+ value = callback.apply( obj[ i ], args );
638
+
639
+ if ( value === false ) {
640
+ break;
641
+ }
642
+ }
643
+ } else {
644
+ for ( i in obj ) {
645
+ value = callback.apply( obj[ i ], args );
646
+
647
+ if ( value === false ) {
648
+ break;
649
+ }
650
+ }
651
+ }
652
+
653
+ // A special, fast, case for the most common use of each
654
+ } else {
655
+ if ( isArray ) {
656
+ for ( ; i < length; i++ ) {
657
+ value = callback.call( obj[ i ], i, obj[ i ] );
658
+
659
+ if ( value === false ) {
660
+ break;
661
+ }
662
+ }
663
+ } else {
664
+ for ( i in obj ) {
665
+ value = callback.call( obj[ i ], i, obj[ i ] );
666
+
667
+ if ( value === false ) {
668
+ break;
669
+ }
670
+ }
671
+ }
672
+ }
673
+
674
+ return obj;
675
+ },
676
+
677
+ // Use native String.trim function wherever possible
678
+ trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
679
+ function( text ) {
680
+ return text == null ?
681
+ "" :
682
+ core_trim.call( text );
683
+ } :
684
+
685
+ // Otherwise use our own trimming functionality
686
+ function( text ) {
687
+ return text == null ?
688
+ "" :
689
+ ( text + "" ).replace( rtrim, "" );
690
+ },
691
+
692
+ // results is for internal usage only
693
+ makeArray: function( arr, results ) {
694
+ var ret = results || [];
695
+
696
+ if ( arr != null ) {
697
+ if ( isArraylike( Object(arr) ) ) {
698
+ jQuery.merge( ret,
699
+ typeof arr === "string" ?
700
+ [ arr ] : arr
701
+ );
702
+ } else {
703
+ core_push.call( ret, arr );
704
+ }
705
+ }
706
+
707
+ return ret;
708
+ },
709
+
710
+ inArray: function( elem, arr, i ) {
711
+ var len;
712
+
713
+ if ( arr ) {
714
+ if ( core_indexOf ) {
715
+ return core_indexOf.call( arr, elem, i );
716
+ }
717
+
718
+ len = arr.length;
719
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
720
+
721
+ for ( ; i < len; i++ ) {
722
+ // Skip accessing in sparse arrays
723
+ if ( i in arr && arr[ i ] === elem ) {
724
+ return i;
725
+ }
726
+ }
727
+ }
728
+
729
+ return -1;
730
+ },
731
+
732
+ merge: function( first, second ) {
733
+ var l = second.length,
734
+ i = first.length,
735
+ j = 0;
736
+
737
+ if ( typeof l === "number" ) {
738
+ for ( ; j < l; j++ ) {
739
+ first[ i++ ] = second[ j ];
740
+ }
741
+ } else {
742
+ while ( second[j] !== undefined ) {
743
+ first[ i++ ] = second[ j++ ];
744
+ }
745
+ }
746
+
747
+ first.length = i;
748
+
749
+ return first;
750
+ },
751
+
752
+ grep: function( elems, callback, inv ) {
753
+ var retVal,
754
+ ret = [],
755
+ i = 0,
756
+ length = elems.length;
757
+ inv = !!inv;
758
+
759
+ // Go through the array, only saving the items
760
+ // that pass the validator function
761
+ for ( ; i < length; i++ ) {
762
+ retVal = !!callback( elems[ i ], i );
763
+ if ( inv !== retVal ) {
764
+ ret.push( elems[ i ] );
765
+ }
766
+ }
767
+
768
+ return ret;
769
+ },
770
+
771
+ // arg is for internal usage only
772
+ map: function( elems, callback, arg ) {
773
+ var value,
774
+ i = 0,
775
+ length = elems.length,
776
+ isArray = isArraylike( elems ),
777
+ ret = [];
778
+
779
+ // Go through the array, translating each of the items to their
780
+ if ( isArray ) {
781
+ for ( ; i < length; i++ ) {
782
+ value = callback( elems[ i ], i, arg );
783
+
784
+ if ( value != null ) {
785
+ ret[ ret.length ] = value;
786
+ }
787
+ }
788
+
789
+ // Go through every key on the object,
790
+ } else {
791
+ for ( i in elems ) {
792
+ value = callback( elems[ i ], i, arg );
793
+
794
+ if ( value != null ) {
795
+ ret[ ret.length ] = value;
796
+ }
797
+ }
798
+ }
799
+
800
+ // Flatten any nested arrays
801
+ return core_concat.apply( [], ret );
802
+ },
803
+
804
+ // A global GUID counter for objects
805
+ guid: 1,
806
+
807
+ // Bind a function to a context, optionally partially applying any
808
+ // arguments.
809
+ proxy: function( fn, context ) {
810
+ var args, proxy, tmp;
811
+
812
+ if ( typeof context === "string" ) {
813
+ tmp = fn[ context ];
814
+ context = fn;
815
+ fn = tmp;
816
+ }
817
+
818
+ // Quick check to determine if target is callable, in the spec
819
+ // this throws a TypeError, but we will just return undefined.
820
+ if ( !jQuery.isFunction( fn ) ) {
821
+ return undefined;
822
+ }
823
+
824
+ // Simulated bind
825
+ args = core_slice.call( arguments, 2 );
826
+ proxy = function() {
827
+ return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
828
+ };
829
+
830
+ // Set the guid of unique handler to the same of original handler, so it can be removed
831
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
832
+
833
+ return proxy;
834
+ },
835
+
836
+ // Multifunctional method to get and set values of a collection
837
+ // The value/s can optionally be executed if it's a function
838
+ access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
839
+ var i = 0,
840
+ length = elems.length,
841
+ bulk = key == null;
842
+
843
+ // Sets many values
844
+ if ( jQuery.type( key ) === "object" ) {
845
+ chainable = true;
846
+ for ( i in key ) {
847
+ jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
848
+ }
849
+
850
+ // Sets one value
851
+ } else if ( value !== undefined ) {
852
+ chainable = true;
853
+
854
+ if ( !jQuery.isFunction( value ) ) {
855
+ raw = true;
856
+ }
857
+
858
+ if ( bulk ) {
859
+ // Bulk operations run against the entire set
860
+ if ( raw ) {
861
+ fn.call( elems, value );
862
+ fn = null;
863
+
864
+ // ...except when executing function values
865
+ } else {
866
+ bulk = fn;
867
+ fn = function( elem, key, value ) {
868
+ return bulk.call( jQuery( elem ), value );
869
+ };
870
+ }
871
+ }
872
+
873
+ if ( fn ) {
874
+ for ( ; i < length; i++ ) {
875
+ fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
876
+ }
877
+ }
878
+ }
879
+
880
+ return chainable ?
881
+ elems :
882
+
883
+ // Gets
884
+ bulk ?
885
+ fn.call( elems ) :
886
+ length ? fn( elems[0], key ) : emptyGet;
887
+ },
888
+
889
+ now: function() {
890
+ return ( new Date() ).getTime();
891
+ },
892
+
893
+ // A method for quickly swapping in/out CSS properties to get correct calculations.
894
+ // Note: this method belongs to the css module but it's needed here for the support module.
895
+ // If support gets modularized, this method should be moved back to the css module.
896
+ swap: function( elem, options, callback, args ) {
897
+ var ret, name,
898
+ old = {};
899
+
900
+ // Remember the old values, and insert the new ones
901
+ for ( name in options ) {
902
+ old[ name ] = elem.style[ name ];
903
+ elem.style[ name ] = options[ name ];
904
+ }
905
+
906
+ ret = callback.apply( elem, args || [] );
907
+
908
+ // Revert the old values
909
+ for ( name in options ) {
910
+ elem.style[ name ] = old[ name ];
911
+ }
912
+
913
+ return ret;
914
+ }
915
+ });
916
+
917
+ jQuery.ready.promise = function( obj ) {
918
+ if ( !readyList ) {
919
+
920
+ readyList = jQuery.Deferred();
921
+
922
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
923
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
924
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
925
+ if ( document.readyState === "complete" ) {
926
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
927
+ setTimeout( jQuery.ready );
928
+
929
+ // Standards-based browsers support DOMContentLoaded
930
+ } else if ( document.addEventListener ) {
931
+ // Use the handy event callback
932
+ document.addEventListener( "DOMContentLoaded", completed, false );
933
+
934
+ // A fallback to window.onload, that will always work
935
+ window.addEventListener( "load", completed, false );
936
+
937
+ // If IE event model is used
938
+ } else {
939
+ // Ensure firing before onload, maybe late but safe also for iframes
940
+ document.attachEvent( "onreadystatechange", completed );
941
+
942
+ // A fallback to window.onload, that will always work
943
+ window.attachEvent( "onload", completed );
944
+
945
+ // If IE and not a frame
946
+ // continually check to see if the document is ready
947
+ var top = false;
948
+
949
+ try {
950
+ top = window.frameElement == null && document.documentElement;
951
+ } catch(e) {}
952
+
953
+ if ( top && top.doScroll ) {
954
+ (function doScrollCheck() {
955
+ if ( !jQuery.isReady ) {
956
+
957
+ try {
958
+ // Use the trick by Diego Perini
959
+ // http://javascript.nwbox.com/IEContentLoaded/
960
+ top.doScroll("left");
961
+ } catch(e) {
962
+ return setTimeout( doScrollCheck, 50 );
963
+ }
964
+
965
+ // detach all dom ready events
966
+ detach();
967
+
968
+ // and execute any waiting functions
969
+ jQuery.ready();
970
+ }
971
+ })();
972
+ }
973
+ }
974
+ }
975
+ return readyList.promise( obj );
976
+ };
977
+
978
+ // Populate the class2type map
979
+ jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
980
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
981
+ });
982
+
983
+ function isArraylike( obj ) {
984
+ var length = obj.length,
985
+ type = jQuery.type( obj );
986
+
987
+ if ( jQuery.isWindow( obj ) ) {
988
+ return false;
989
+ }
990
+
991
+ if ( obj.nodeType === 1 && length ) {
992
+ return true;
993
+ }
994
+
995
+ return type === "array" || type !== "function" &&
996
+ ( length === 0 ||
997
+ typeof length === "number" && length > 0 && ( length - 1 ) in obj );
998
+ }
999
+
1000
+ // All jQuery objects should point back to these
1001
+ rootjQuery = jQuery(document);
1002
+ /*!
1003
+ * Sizzle CSS Selector Engine v1.10.2
1004
+ * http://sizzlejs.com/
1005
+ *
1006
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
1007
+ * Released under the MIT license
1008
+ * http://jquery.org/license
1009
+ *
1010
+ * Date: 2013-07-03
1011
+ */
1012
+ (function( window, undefined ) {
1013
+
1014
+ var i,
1015
+ support,
1016
+ cachedruns,
1017
+ Expr,
1018
+ getText,
1019
+ isXML,
1020
+ compile,
1021
+ outermostContext,
1022
+ sortInput,
1023
+
1024
+ // Local document vars
1025
+ setDocument,
1026
+ document,
1027
+ docElem,
1028
+ documentIsHTML,
1029
+ rbuggyQSA,
1030
+ rbuggyMatches,
1031
+ matches,
1032
+ contains,
1033
+
1034
+ // Instance-specific data
1035
+ expando = "sizzle" + -(new Date()),
1036
+ preferredDoc = window.document,
1037
+ dirruns = 0,
1038
+ done = 0,
1039
+ classCache = createCache(),
1040
+ tokenCache = createCache(),
1041
+ compilerCache = createCache(),
1042
+ hasDuplicate = false,
1043
+ sortOrder = function( a, b ) {
1044
+ if ( a === b ) {
1045
+ hasDuplicate = true;
1046
+ return 0;
1047
+ }
1048
+ return 0;
1049
+ },
1050
+
1051
+ // General-purpose constants
1052
+ strundefined = typeof undefined,
1053
+ MAX_NEGATIVE = 1 << 31,
1054
+
1055
+ // Instance methods
1056
+ hasOwn = ({}).hasOwnProperty,
1057
+ arr = [],
1058
+ pop = arr.pop,
1059
+ push_native = arr.push,
1060
+ push = arr.push,
1061
+ slice = arr.slice,
1062
+ // Use a stripped-down indexOf if we can't use a native one
1063
+ indexOf = arr.indexOf || function( elem ) {
1064
+ var i = 0,
1065
+ len = this.length;
1066
+ for ( ; i < len; i++ ) {
1067
+ if ( this[i] === elem ) {
1068
+ return i;
1069
+ }
1070
+ }
1071
+ return -1;
1072
+ },
1073
+
1074
+ booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
1075
+
1076
+ // Regular expressions
1077
+
1078
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
1079
+ whitespace = "[\\x20\\t\\r\\n\\f]",
1080
+ // http://www.w3.org/TR/css3-syntax/#characters
1081
+ characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
1082
+
1083
+ // Loosely modeled on CSS identifier characters
1084
+ // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
1085
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
1086
+ identifier = characterEncoding.replace( "w", "w#" ),
1087
+
1088
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
1089
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
1090
+ "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
1091
+
1092
+ // Prefer arguments quoted,
1093
+ // then not containing pseudos/brackets,
1094
+ // then attribute selectors/non-parenthetical expressions,
1095
+ // then anything else
1096
+ // These preferences are here to reduce the number of selectors
1097
+ // needing tokenize in the PSEUDO preFilter
1098
+ pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
1099
+
1100
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
1101
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
1102
+
1103
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
1104
+ rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
1105
+
1106
+ rsibling = new RegExp( whitespace + "*[+~]" ),
1107
+ rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ),
1108
+
1109
+ rpseudo = new RegExp( pseudos ),
1110
+ ridentifier = new RegExp( "^" + identifier + "$" ),
1111
+
1112
+ matchExpr = {
1113
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
1114
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
1115
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
1116
+ "ATTR": new RegExp( "^" + attributes ),
1117
+ "PSEUDO": new RegExp( "^" + pseudos ),
1118
+ "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
1119
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
1120
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
1121
+ "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
1122
+ // For use in libraries implementing .is()
1123
+ // We use this for POS matching in `select`
1124
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
1125
+ whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
1126
+ },
1127
+
1128
+ rnative = /^[^{]+\{\s*\[native \w/,
1129
+
1130
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
1131
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
1132
+
1133
+ rinputs = /^(?:input|select|textarea|button)$/i,
1134
+ rheader = /^h\d$/i,
1135
+
1136
+ rescape = /'|\\/g,
1137
+
1138
+ // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
1139
+ runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
1140
+ funescape = function( _, escaped, escapedWhitespace ) {
1141
+ var high = "0x" + escaped - 0x10000;
1142
+ // NaN means non-codepoint
1143
+ // Support: Firefox
1144
+ // Workaround erroneous numeric interpretation of +"0x"
1145
+ return high !== high || escapedWhitespace ?
1146
+ escaped :
1147
+ // BMP codepoint
1148
+ high < 0 ?
1149
+ String.fromCharCode( high + 0x10000 ) :
1150
+ // Supplemental Plane codepoint (surrogate pair)
1151
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
1152
+ };
1153
+
1154
+ // Optimize for push.apply( _, NodeList )
1155
+ try {
1156
+ push.apply(
1157
+ (arr = slice.call( preferredDoc.childNodes )),
1158
+ preferredDoc.childNodes
1159
+ );
1160
+ // Support: Android<4.0
1161
+ // Detect silently failing push.apply
1162
+ arr[ preferredDoc.childNodes.length ].nodeType;
1163
+ } catch ( e ) {
1164
+ push = { apply: arr.length ?
1165
+
1166
+ // Leverage slice if possible
1167
+ function( target, els ) {
1168
+ push_native.apply( target, slice.call(els) );
1169
+ } :
1170
+
1171
+ // Support: IE<9
1172
+ // Otherwise append directly
1173
+ function( target, els ) {
1174
+ var j = target.length,
1175
+ i = 0;
1176
+ // Can't trust NodeList.length
1177
+ while ( (target[j++] = els[i++]) ) {}
1178
+ target.length = j - 1;
1179
+ }
1180
+ };
1181
+ }
1182
+
1183
+ function Sizzle( selector, context, results, seed ) {
1184
+ var match, elem, m, nodeType,
1185
+ // QSA vars
1186
+ i, groups, old, nid, newContext, newSelector;
1187
+
1188
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
1189
+ setDocument( context );
1190
+ }
1191
+
1192
+ context = context || document;
1193
+ results = results || [];
1194
+
1195
+ if ( !selector || typeof selector !== "string" ) {
1196
+ return results;
1197
+ }
1198
+
1199
+ if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
1200
+ return [];
1201
+ }
1202
+
1203
+ if ( documentIsHTML && !seed ) {
1204
+
1205
+ // Shortcuts
1206
+ if ( (match = rquickExpr.exec( selector )) ) {
1207
+ // Speed-up: Sizzle("#ID")
1208
+ if ( (m = match[1]) ) {
1209
+ if ( nodeType === 9 ) {
1210
+ elem = context.getElementById( m );
1211
+ // Check parentNode to catch when Blackberry 4.6 returns
1212
+ // nodes that are no longer in the document #6963
1213
+ if ( elem && elem.parentNode ) {
1214
+ // Handle the case where IE, Opera, and Webkit return items
1215
+ // by name instead of ID
1216
+ if ( elem.id === m ) {
1217
+ results.push( elem );
1218
+ return results;
1219
+ }
1220
+ } else {
1221
+ return results;
1222
+ }
1223
+ } else {
1224
+ // Context is not a document
1225
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
1226
+ contains( context, elem ) && elem.id === m ) {
1227
+ results.push( elem );
1228
+ return results;
1229
+ }
1230
+ }
1231
+
1232
+ // Speed-up: Sizzle("TAG")
1233
+ } else if ( match[2] ) {
1234
+ push.apply( results, context.getElementsByTagName( selector ) );
1235
+ return results;
1236
+
1237
+ // Speed-up: Sizzle(".CLASS")
1238
+ } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
1239
+ push.apply( results, context.getElementsByClassName( m ) );
1240
+ return results;
1241
+ }
1242
+ }
1243
+
1244
+ // QSA path
1245
+ if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
1246
+ nid = old = expando;
1247
+ newContext = context;
1248
+ newSelector = nodeType === 9 && selector;
1249
+
1250
+ // qSA works strangely on Element-rooted queries
1251
+ // We can work around this by specifying an extra ID on the root
1252
+ // and working up from there (Thanks to Andrew Dupont for the technique)
1253
+ // IE 8 doesn't work on object elements
1254
+ if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
1255
+ groups = tokenize( selector );
1256
+
1257
+ if ( (old = context.getAttribute("id")) ) {
1258
+ nid = old.replace( rescape, "\\$&" );
1259
+ } else {
1260
+ context.setAttribute( "id", nid );
1261
+ }
1262
+ nid = "[id='" + nid + "'] ";
1263
+
1264
+ i = groups.length;
1265
+ while ( i-- ) {
1266
+ groups[i] = nid + toSelector( groups[i] );
1267
+ }
1268
+ newContext = rsibling.test( selector ) && context.parentNode || context;
1269
+ newSelector = groups.join(",");
1270
+ }
1271
+
1272
+ if ( newSelector ) {
1273
+ try {
1274
+ push.apply( results,
1275
+ newContext.querySelectorAll( newSelector )
1276
+ );
1277
+ return results;
1278
+ } catch(qsaError) {
1279
+ } finally {
1280
+ if ( !old ) {
1281
+ context.removeAttribute("id");
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+ }
1287
+
1288
+ // All others
1289
+ return select( selector.replace( rtrim, "$1" ), context, results, seed );
1290
+ }
1291
+
1292
+ /**
1293
+ * Create key-value caches of limited size
1294
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
1295
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
1296
+ * deleting the oldest entry
1297
+ */
1298
+ function createCache() {
1299
+ var keys = [];
1300
+
1301
+ function cache( key, value ) {
1302
+ // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
1303
+ if ( keys.push( key += " " ) > Expr.cacheLength ) {
1304
+ // Only keep the most recent entries
1305
+ delete cache[ keys.shift() ];
1306
+ }
1307
+ return (cache[ key ] = value);
1308
+ }
1309
+ return cache;
1310
+ }
1311
+
1312
+ /**
1313
+ * Mark a function for special use by Sizzle
1314
+ * @param {Function} fn The function to mark
1315
+ */
1316
+ function markFunction( fn ) {
1317
+ fn[ expando ] = true;
1318
+ return fn;
1319
+ }
1320
+
1321
+ /**
1322
+ * Support testing using an element
1323
+ * @param {Function} fn Passed the created div and expects a boolean result
1324
+ */
1325
+ function assert( fn ) {
1326
+ var div = document.createElement("div");
1327
+
1328
+ try {
1329
+ return !!fn( div );
1330
+ } catch (e) {
1331
+ return false;
1332
+ } finally {
1333
+ // Remove from its parent by default
1334
+ if ( div.parentNode ) {
1335
+ div.parentNode.removeChild( div );
1336
+ }
1337
+ // release memory in IE
1338
+ div = null;
1339
+ }
1340
+ }
1341
+
1342
+ /**
1343
+ * Adds the same handler for all of the specified attrs
1344
+ * @param {String} attrs Pipe-separated list of attributes
1345
+ * @param {Function} handler The method that will be applied
1346
+ */
1347
+ function addHandle( attrs, handler ) {
1348
+ var arr = attrs.split("|"),
1349
+ i = attrs.length;
1350
+
1351
+ while ( i-- ) {
1352
+ Expr.attrHandle[ arr[i] ] = handler;
1353
+ }
1354
+ }
1355
+
1356
+ /**
1357
+ * Checks document order of two siblings
1358
+ * @param {Element} a
1359
+ * @param {Element} b
1360
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
1361
+ */
1362
+ function siblingCheck( a, b ) {
1363
+ var cur = b && a,
1364
+ diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
1365
+ ( ~b.sourceIndex || MAX_NEGATIVE ) -
1366
+ ( ~a.sourceIndex || MAX_NEGATIVE );
1367
+
1368
+ // Use IE sourceIndex if available on both nodes
1369
+ if ( diff ) {
1370
+ return diff;
1371
+ }
1372
+
1373
+ // Check if b follows a
1374
+ if ( cur ) {
1375
+ while ( (cur = cur.nextSibling) ) {
1376
+ if ( cur === b ) {
1377
+ return -1;
1378
+ }
1379
+ }
1380
+ }
1381
+
1382
+ return a ? 1 : -1;
1383
+ }
1384
+
1385
+ /**
1386
+ * Returns a function to use in pseudos for input types
1387
+ * @param {String} type
1388
+ */
1389
+ function createInputPseudo( type ) {
1390
+ return function( elem ) {
1391
+ var name = elem.nodeName.toLowerCase();
1392
+ return name === "input" && elem.type === type;
1393
+ };
1394
+ }
1395
+
1396
+ /**
1397
+ * Returns a function to use in pseudos for buttons
1398
+ * @param {String} type
1399
+ */
1400
+ function createButtonPseudo( type ) {
1401
+ return function( elem ) {
1402
+ var name = elem.nodeName.toLowerCase();
1403
+ return (name === "input" || name === "button") && elem.type === type;
1404
+ };
1405
+ }
1406
+
1407
+ /**
1408
+ * Returns a function to use in pseudos for positionals
1409
+ * @param {Function} fn
1410
+ */
1411
+ function createPositionalPseudo( fn ) {
1412
+ return markFunction(function( argument ) {
1413
+ argument = +argument;
1414
+ return markFunction(function( seed, matches ) {
1415
+ var j,
1416
+ matchIndexes = fn( [], seed.length, argument ),
1417
+ i = matchIndexes.length;
1418
+
1419
+ // Match elements found at the specified indexes
1420
+ while ( i-- ) {
1421
+ if ( seed[ (j = matchIndexes[i]) ] ) {
1422
+ seed[j] = !(matches[j] = seed[j]);
1423
+ }
1424
+ }
1425
+ });
1426
+ });
1427
+ }
1428
+
1429
+ /**
1430
+ * Detect xml
1431
+ * @param {Element|Object} elem An element or a document
1432
+ */
1433
+ isXML = Sizzle.isXML = function( elem ) {
1434
+ // documentElement is verified for cases where it doesn't yet exist
1435
+ // (such as loading iframes in IE - #4833)
1436
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
1437
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
1438
+ };
1439
+
1440
+ // Expose support vars for convenience
1441
+ support = Sizzle.support = {};
1442
+
1443
+ /**
1444
+ * Sets document-related variables once based on the current document
1445
+ * @param {Element|Object} [doc] An element or document object to use to set the document
1446
+ * @returns {Object} Returns the current document
1447
+ */
1448
+ setDocument = Sizzle.setDocument = function( node ) {
1449
+ var doc = node ? node.ownerDocument || node : preferredDoc,
1450
+ parent = doc.defaultView;
1451
+
1452
+ // If no document and documentElement is available, return
1453
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
1454
+ return document;
1455
+ }
1456
+
1457
+ // Set our document
1458
+ document = doc;
1459
+ docElem = doc.documentElement;
1460
+
1461
+ // Support tests
1462
+ documentIsHTML = !isXML( doc );
1463
+
1464
+ // Support: IE>8
1465
+ // If iframe document is assigned to "document" variable and if iframe has been reloaded,
1466
+ // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
1467
+ // IE6-8 do not support the defaultView property so parent will be undefined
1468
+ if ( parent && parent.attachEvent && parent !== parent.top ) {
1469
+ parent.attachEvent( "onbeforeunload", function() {
1470
+ setDocument();
1471
+ });
1472
+ }
1473
+
1474
+ /* Attributes
1475
+ ---------------------------------------------------------------------- */
1476
+
1477
+ // Support: IE<8
1478
+ // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
1479
+ support.attributes = assert(function( div ) {
1480
+ div.className = "i";
1481
+ return !div.getAttribute("className");
1482
+ });
1483
+
1484
+ /* getElement(s)By*
1485
+ ---------------------------------------------------------------------- */
1486
+
1487
+ // Check if getElementsByTagName("*") returns only elements
1488
+ support.getElementsByTagName = assert(function( div ) {
1489
+ div.appendChild( doc.createComment("") );
1490
+ return !div.getElementsByTagName("*").length;
1491
+ });
1492
+
1493
+ // Check if getElementsByClassName can be trusted
1494
+ support.getElementsByClassName = assert(function( div ) {
1495
+ div.innerHTML = "<div class='a'></div><div class='a i'></div>";
1496
+
1497
+ // Support: Safari<4
1498
+ // Catch class over-caching
1499
+ div.firstChild.className = "i";
1500
+ // Support: Opera<10
1501
+ // Catch gEBCN failure to find non-leading classes
1502
+ return div.getElementsByClassName("i").length === 2;
1503
+ });
1504
+
1505
+ // Support: IE<10
1506
+ // Check if getElementById returns elements by name
1507
+ // The broken getElementById methods don't pick up programatically-set names,
1508
+ // so use a roundabout getElementsByName test
1509
+ support.getById = assert(function( div ) {
1510
+ docElem.appendChild( div ).id = expando;
1511
+ return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
1512
+ });
1513
+
1514
+ // ID find and filter
1515
+ if ( support.getById ) {
1516
+ Expr.find["ID"] = function( id, context ) {
1517
+ if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
1518
+ var m = context.getElementById( id );
1519
+ // Check parentNode to catch when Blackberry 4.6 returns
1520
+ // nodes that are no longer in the document #6963
1521
+ return m && m.parentNode ? [m] : [];
1522
+ }
1523
+ };
1524
+ Expr.filter["ID"] = function( id ) {
1525
+ var attrId = id.replace( runescape, funescape );
1526
+ return function( elem ) {
1527
+ return elem.getAttribute("id") === attrId;
1528
+ };
1529
+ };
1530
+ } else {
1531
+ // Support: IE6/7
1532
+ // getElementById is not reliable as a find shortcut
1533
+ delete Expr.find["ID"];
1534
+
1535
+ Expr.filter["ID"] = function( id ) {
1536
+ var attrId = id.replace( runescape, funescape );
1537
+ return function( elem ) {
1538
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
1539
+ return node && node.value === attrId;
1540
+ };
1541
+ };
1542
+ }
1543
+
1544
+ // Tag
1545
+ Expr.find["TAG"] = support.getElementsByTagName ?
1546
+ function( tag, context ) {
1547
+ if ( typeof context.getElementsByTagName !== strundefined ) {
1548
+ return context.getElementsByTagName( tag );
1549
+ }
1550
+ } :
1551
+ function( tag, context ) {
1552
+ var elem,
1553
+ tmp = [],
1554
+ i = 0,
1555
+ results = context.getElementsByTagName( tag );
1556
+
1557
+ // Filter out possible comments
1558
+ if ( tag === "*" ) {
1559
+ while ( (elem = results[i++]) ) {
1560
+ if ( elem.nodeType === 1 ) {
1561
+ tmp.push( elem );
1562
+ }
1563
+ }
1564
+
1565
+ return tmp;
1566
+ }
1567
+ return results;
1568
+ };
1569
+
1570
+ // Class
1571
+ Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
1572
+ if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
1573
+ return context.getElementsByClassName( className );
1574
+ }
1575
+ };
1576
+
1577
+ /* QSA/matchesSelector
1578
+ ---------------------------------------------------------------------- */
1579
+
1580
+ // QSA and matchesSelector support
1581
+
1582
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
1583
+ rbuggyMatches = [];
1584
+
1585
+ // qSa(:focus) reports false when true (Chrome 21)
1586
+ // We allow this because of a bug in IE8/9 that throws an error
1587
+ // whenever `document.activeElement` is accessed on an iframe
1588
+ // So, we allow :focus to pass through QSA all the time to avoid the IE error
1589
+ // See http://bugs.jquery.com/ticket/13378
1590
+ rbuggyQSA = [];
1591
+
1592
+ if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
1593
+ // Build QSA regex
1594
+ // Regex strategy adopted from Diego Perini
1595
+ assert(function( div ) {
1596
+ // Select is set to empty string on purpose
1597
+ // This is to test IE's treatment of not explicitly
1598
+ // setting a boolean content attribute,
1599
+ // since its presence should be enough
1600
+ // http://bugs.jquery.com/ticket/12359
1601
+ div.innerHTML = "<select><option selected=''></option></select>";
1602
+
1603
+ // Support: IE8
1604
+ // Boolean attributes and "value" are not treated correctly
1605
+ if ( !div.querySelectorAll("[selected]").length ) {
1606
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
1607
+ }
1608
+
1609
+ // Webkit/Opera - :checked should return selected option elements
1610
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1611
+ // IE8 throws error here and will not see later tests
1612
+ if ( !div.querySelectorAll(":checked").length ) {
1613
+ rbuggyQSA.push(":checked");
1614
+ }
1615
+ });
1616
+
1617
+ assert(function( div ) {
1618
+
1619
+ // Support: Opera 10-12/IE8
1620
+ // ^= $= *= and empty values
1621
+ // Should not select anything
1622
+ // Support: Windows 8 Native Apps
1623
+ // The type attribute is restricted during .innerHTML assignment
1624
+ var input = doc.createElement("input");
1625
+ input.setAttribute( "type", "hidden" );
1626
+ div.appendChild( input ).setAttribute( "t", "" );
1627
+
1628
+ if ( div.querySelectorAll("[t^='']").length ) {
1629
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
1630
+ }
1631
+
1632
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
1633
+ // IE8 throws error here and will not see later tests
1634
+ if ( !div.querySelectorAll(":enabled").length ) {
1635
+ rbuggyQSA.push( ":enabled", ":disabled" );
1636
+ }
1637
+
1638
+ // Opera 10-11 does not throw on post-comma invalid pseudos
1639
+ div.querySelectorAll("*,:x");
1640
+ rbuggyQSA.push(",.*:");
1641
+ });
1642
+ }
1643
+
1644
+ if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
1645
+ docElem.mozMatchesSelector ||
1646
+ docElem.oMatchesSelector ||
1647
+ docElem.msMatchesSelector) )) ) {
1648
+
1649
+ assert(function( div ) {
1650
+ // Check to see if it's possible to do matchesSelector
1651
+ // on a disconnected node (IE 9)
1652
+ support.disconnectedMatch = matches.call( div, "div" );
1653
+
1654
+ // This should fail with an exception
1655
+ // Gecko does not error, returns false instead
1656
+ matches.call( div, "[s!='']:x" );
1657
+ rbuggyMatches.push( "!=", pseudos );
1658
+ });
1659
+ }
1660
+
1661
+ rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
1662
+ rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
1663
+
1664
+ /* Contains
1665
+ ---------------------------------------------------------------------- */
1666
+
1667
+ // Element contains another
1668
+ // Purposefully does not implement inclusive descendent
1669
+ // As in, an element does not contain itself
1670
+ contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ?
1671
+ function( a, b ) {
1672
+ var adown = a.nodeType === 9 ? a.documentElement : a,
1673
+ bup = b && b.parentNode;
1674
+ return a === bup || !!( bup && bup.nodeType === 1 && (
1675
+ adown.contains ?
1676
+ adown.contains( bup ) :
1677
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
1678
+ ));
1679
+ } :
1680
+ function( a, b ) {
1681
+ if ( b ) {
1682
+ while ( (b = b.parentNode) ) {
1683
+ if ( b === a ) {
1684
+ return true;
1685
+ }
1686
+ }
1687
+ }
1688
+ return false;
1689
+ };
1690
+
1691
+ /* Sorting
1692
+ ---------------------------------------------------------------------- */
1693
+
1694
+ // Document order sorting
1695
+ sortOrder = docElem.compareDocumentPosition ?
1696
+ function( a, b ) {
1697
+
1698
+ // Flag for duplicate removal
1699
+ if ( a === b ) {
1700
+ hasDuplicate = true;
1701
+ return 0;
1702
+ }
1703
+
1704
+ var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
1705
+
1706
+ if ( compare ) {
1707
+ // Disconnected nodes
1708
+ if ( compare & 1 ||
1709
+ (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
1710
+
1711
+ // Choose the first element that is related to our preferred document
1712
+ if ( a === doc || contains(preferredDoc, a) ) {
1713
+ return -1;
1714
+ }
1715
+ if ( b === doc || contains(preferredDoc, b) ) {
1716
+ return 1;
1717
+ }
1718
+
1719
+ // Maintain original order
1720
+ return sortInput ?
1721
+ ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
1722
+ 0;
1723
+ }
1724
+
1725
+ return compare & 4 ? -1 : 1;
1726
+ }
1727
+
1728
+ // Not directly comparable, sort on existence of method
1729
+ return a.compareDocumentPosition ? -1 : 1;
1730
+ } :
1731
+ function( a, b ) {
1732
+ var cur,
1733
+ i = 0,
1734
+ aup = a.parentNode,
1735
+ bup = b.parentNode,
1736
+ ap = [ a ],
1737
+ bp = [ b ];
1738
+
1739
+ // Exit early if the nodes are identical
1740
+ if ( a === b ) {
1741
+ hasDuplicate = true;
1742
+ return 0;
1743
+
1744
+ // Parentless nodes are either documents or disconnected
1745
+ } else if ( !aup || !bup ) {
1746
+ return a === doc ? -1 :
1747
+ b === doc ? 1 :
1748
+ aup ? -1 :
1749
+ bup ? 1 :
1750
+ sortInput ?
1751
+ ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
1752
+ 0;
1753
+
1754
+ // If the nodes are siblings, we can do a quick check
1755
+ } else if ( aup === bup ) {
1756
+ return siblingCheck( a, b );
1757
+ }
1758
+
1759
+ // Otherwise we need full lists of their ancestors for comparison
1760
+ cur = a;
1761
+ while ( (cur = cur.parentNode) ) {
1762
+ ap.unshift( cur );
1763
+ }
1764
+ cur = b;
1765
+ while ( (cur = cur.parentNode) ) {
1766
+ bp.unshift( cur );
1767
+ }
1768
+
1769
+ // Walk down the tree looking for a discrepancy
1770
+ while ( ap[i] === bp[i] ) {
1771
+ i++;
1772
+ }
1773
+
1774
+ return i ?
1775
+ // Do a sibling check if the nodes have a common ancestor
1776
+ siblingCheck( ap[i], bp[i] ) :
1777
+
1778
+ // Otherwise nodes in our document sort first
1779
+ ap[i] === preferredDoc ? -1 :
1780
+ bp[i] === preferredDoc ? 1 :
1781
+ 0;
1782
+ };
1783
+
1784
+ return doc;
1785
+ };
1786
+
1787
+ Sizzle.matches = function( expr, elements ) {
1788
+ return Sizzle( expr, null, null, elements );
1789
+ };
1790
+
1791
+ Sizzle.matchesSelector = function( elem, expr ) {
1792
+ // Set document vars if needed
1793
+ if ( ( elem.ownerDocument || elem ) !== document ) {
1794
+ setDocument( elem );
1795
+ }
1796
+
1797
+ // Make sure that attribute selectors are quoted
1798
+ expr = expr.replace( rattributeQuotes, "='$1']" );
1799
+
1800
+ if ( support.matchesSelector && documentIsHTML &&
1801
+ ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
1802
+ ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
1803
+
1804
+ try {
1805
+ var ret = matches.call( elem, expr );
1806
+
1807
+ // IE 9's matchesSelector returns false on disconnected nodes
1808
+ if ( ret || support.disconnectedMatch ||
1809
+ // As well, disconnected nodes are said to be in a document
1810
+ // fragment in IE 9
1811
+ elem.document && elem.document.nodeType !== 11 ) {
1812
+ return ret;
1813
+ }
1814
+ } catch(e) {}
1815
+ }
1816
+
1817
+ return Sizzle( expr, document, null, [elem] ).length > 0;
1818
+ };
1819
+
1820
+ Sizzle.contains = function( context, elem ) {
1821
+ // Set document vars if needed
1822
+ if ( ( context.ownerDocument || context ) !== document ) {
1823
+ setDocument( context );
1824
+ }
1825
+ return contains( context, elem );
1826
+ };
1827
+
1828
+ Sizzle.attr = function( elem, name ) {
1829
+ // Set document vars if needed
1830
+ if ( ( elem.ownerDocument || elem ) !== document ) {
1831
+ setDocument( elem );
1832
+ }
1833
+
1834
+ var fn = Expr.attrHandle[ name.toLowerCase() ],
1835
+ // Don't get fooled by Object.prototype properties (jQuery #13807)
1836
+ val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
1837
+ fn( elem, name, !documentIsHTML ) :
1838
+ undefined;
1839
+
1840
+ return val === undefined ?
1841
+ support.attributes || !documentIsHTML ?
1842
+ elem.getAttribute( name ) :
1843
+ (val = elem.getAttributeNode(name)) && val.specified ?
1844
+ val.value :
1845
+ null :
1846
+ val;
1847
+ };
1848
+
1849
+ Sizzle.error = function( msg ) {
1850
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
1851
+ };
1852
+
1853
+ /**
1854
+ * Document sorting and removing duplicates
1855
+ * @param {ArrayLike} results
1856
+ */
1857
+ Sizzle.uniqueSort = function( results ) {
1858
+ var elem,
1859
+ duplicates = [],
1860
+ j = 0,
1861
+ i = 0;
1862
+
1863
+ // Unless we *know* we can detect duplicates, assume their presence
1864
+ hasDuplicate = !support.detectDuplicates;
1865
+ sortInput = !support.sortStable && results.slice( 0 );
1866
+ results.sort( sortOrder );
1867
+
1868
+ if ( hasDuplicate ) {
1869
+ while ( (elem = results[i++]) ) {
1870
+ if ( elem === results[ i ] ) {
1871
+ j = duplicates.push( i );
1872
+ }
1873
+ }
1874
+ while ( j-- ) {
1875
+ results.splice( duplicates[ j ], 1 );
1876
+ }
1877
+ }
1878
+
1879
+ return results;
1880
+ };
1881
+
1882
+ /**
1883
+ * Utility function for retrieving the text value of an array of DOM nodes
1884
+ * @param {Array|Element} elem
1885
+ */
1886
+ getText = Sizzle.getText = function( elem ) {
1887
+ var node,
1888
+ ret = "",
1889
+ i = 0,
1890
+ nodeType = elem.nodeType;
1891
+
1892
+ if ( !nodeType ) {
1893
+ // If no nodeType, this is expected to be an array
1894
+ for ( ; (node = elem[i]); i++ ) {
1895
+ // Do not traverse comment nodes
1896
+ ret += getText( node );
1897
+ }
1898
+ } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
1899
+ // Use textContent for elements
1900
+ // innerText usage removed for consistency of new lines (see #11153)
1901
+ if ( typeof elem.textContent === "string" ) {
1902
+ return elem.textContent;
1903
+ } else {
1904
+ // Traverse its children
1905
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1906
+ ret += getText( elem );
1907
+ }
1908
+ }
1909
+ } else if ( nodeType === 3 || nodeType === 4 ) {
1910
+ return elem.nodeValue;
1911
+ }
1912
+ // Do not include comment or processing instruction nodes
1913
+
1914
+ return ret;
1915
+ };
1916
+
1917
+ Expr = Sizzle.selectors = {
1918
+
1919
+ // Can be adjusted by the user
1920
+ cacheLength: 50,
1921
+
1922
+ createPseudo: markFunction,
1923
+
1924
+ match: matchExpr,
1925
+
1926
+ attrHandle: {},
1927
+
1928
+ find: {},
1929
+
1930
+ relative: {
1931
+ ">": { dir: "parentNode", first: true },
1932
+ " ": { dir: "parentNode" },
1933
+ "+": { dir: "previousSibling", first: true },
1934
+ "~": { dir: "previousSibling" }
1935
+ },
1936
+
1937
+ preFilter: {
1938
+ "ATTR": function( match ) {
1939
+ match[1] = match[1].replace( runescape, funescape );
1940
+
1941
+ // Move the given value to match[3] whether quoted or unquoted
1942
+ match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
1943
+
1944
+ if ( match[2] === "~=" ) {
1945
+ match[3] = " " + match[3] + " ";
1946
+ }
1947
+
1948
+ return match.slice( 0, 4 );
1949
+ },
1950
+
1951
+ "CHILD": function( match ) {
1952
+ /* matches from matchExpr["CHILD"]
1953
+ 1 type (only|nth|...)
1954
+ 2 what (child|of-type)
1955
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
1956
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
1957
+ 5 sign of xn-component
1958
+ 6 x of xn-component
1959
+ 7 sign of y-component
1960
+ 8 y of y-component
1961
+ */
1962
+ match[1] = match[1].toLowerCase();
1963
+
1964
+ if ( match[1].slice( 0, 3 ) === "nth" ) {
1965
+ // nth-* requires argument
1966
+ if ( !match[3] ) {
1967
+ Sizzle.error( match[0] );
1968
+ }
1969
+
1970
+ // numeric x and y parameters for Expr.filter.CHILD
1971
+ // remember that false/true cast respectively to 0/1
1972
+ match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
1973
+ match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
1974
+
1975
+ // other types prohibit arguments
1976
+ } else if ( match[3] ) {
1977
+ Sizzle.error( match[0] );
1978
+ }
1979
+
1980
+ return match;
1981
+ },
1982
+
1983
+ "PSEUDO": function( match ) {
1984
+ var excess,
1985
+ unquoted = !match[5] && match[2];
1986
+
1987
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
1988
+ return null;
1989
+ }
1990
+
1991
+ // Accept quoted arguments as-is
1992
+ if ( match[3] && match[4] !== undefined ) {
1993
+ match[2] = match[4];
1994
+
1995
+ // Strip excess characters from unquoted arguments
1996
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
1997
+ // Get excess from tokenize (recursively)
1998
+ (excess = tokenize( unquoted, true )) &&
1999
+ // advance to the next closing parenthesis
2000
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
2001
+
2002
+ // excess is a negative index
2003
+ match[0] = match[0].slice( 0, excess );
2004
+ match[2] = unquoted.slice( 0, excess );
2005
+ }
2006
+
2007
+ // Return only captures needed by the pseudo filter method (type and argument)
2008
+ return match.slice( 0, 3 );
2009
+ }
2010
+ },
2011
+
2012
+ filter: {
2013
+
2014
+ "TAG": function( nodeNameSelector ) {
2015
+ var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
2016
+ return nodeNameSelector === "*" ?
2017
+ function() { return true; } :
2018
+ function( elem ) {
2019
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
2020
+ };
2021
+ },
2022
+
2023
+ "CLASS": function( className ) {
2024
+ var pattern = classCache[ className + " " ];
2025
+
2026
+ return pattern ||
2027
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
2028
+ classCache( className, function( elem ) {
2029
+ return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
2030
+ });
2031
+ },
2032
+
2033
+ "ATTR": function( name, operator, check ) {
2034
+ return function( elem ) {
2035
+ var result = Sizzle.attr( elem, name );
2036
+
2037
+ if ( result == null ) {
2038
+ return operator === "!=";
2039
+ }
2040
+ if ( !operator ) {
2041
+ return true;
2042
+ }
2043
+
2044
+ result += "";
2045
+
2046
+ return operator === "=" ? result === check :
2047
+ operator === "!=" ? result !== check :
2048
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
2049
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
2050
+ operator === "$=" ? check && result.slice( -check.length ) === check :
2051
+ operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
2052
+ operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
2053
+ false;
2054
+ };
2055
+ },
2056
+
2057
+ "CHILD": function( type, what, argument, first, last ) {
2058
+ var simple = type.slice( 0, 3 ) !== "nth",
2059
+ forward = type.slice( -4 ) !== "last",
2060
+ ofType = what === "of-type";
2061
+
2062
+ return first === 1 && last === 0 ?
2063
+
2064
+ // Shortcut for :nth-*(n)
2065
+ function( elem ) {
2066
+ return !!elem.parentNode;
2067
+ } :
2068
+
2069
+ function( elem, context, xml ) {
2070
+ var cache, outerCache, node, diff, nodeIndex, start,
2071
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
2072
+ parent = elem.parentNode,
2073
+ name = ofType && elem.nodeName.toLowerCase(),
2074
+ useCache = !xml && !ofType;
2075
+
2076
+ if ( parent ) {
2077
+
2078
+ // :(first|last|only)-(child|of-type)
2079
+ if ( simple ) {
2080
+ while ( dir ) {
2081
+ node = elem;
2082
+ while ( (node = node[ dir ]) ) {
2083
+ if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
2084
+ return false;
2085
+ }
2086
+ }
2087
+ // Reverse direction for :only-* (if we haven't yet done so)
2088
+ start = dir = type === "only" && !start && "nextSibling";
2089
+ }
2090
+ return true;
2091
+ }
2092
+
2093
+ start = [ forward ? parent.firstChild : parent.lastChild ];
2094
+
2095
+ // non-xml :nth-child(...) stores cache data on `parent`
2096
+ if ( forward && useCache ) {
2097
+ // Seek `elem` from a previously-cached index
2098
+ outerCache = parent[ expando ] || (parent[ expando ] = {});
2099
+ cache = outerCache[ type ] || [];
2100
+ nodeIndex = cache[0] === dirruns && cache[1];
2101
+ diff = cache[0] === dirruns && cache[2];
2102
+ node = nodeIndex && parent.childNodes[ nodeIndex ];
2103
+
2104
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
2105
+
2106
+ // Fallback to seeking `elem` from the start
2107
+ (diff = nodeIndex = 0) || start.pop()) ) {
2108
+
2109
+ // When found, cache indexes on `parent` and break
2110
+ if ( node.nodeType === 1 && ++diff && node === elem ) {
2111
+ outerCache[ type ] = [ dirruns, nodeIndex, diff ];
2112
+ break;
2113
+ }
2114
+ }
2115
+
2116
+ // Use previously-cached element index if available
2117
+ } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
2118
+ diff = cache[1];
2119
+
2120
+ // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
2121
+ } else {
2122
+ // Use the same loop as above to seek `elem` from the start
2123
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
2124
+ (diff = nodeIndex = 0) || start.pop()) ) {
2125
+
2126
+ if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
2127
+ // Cache the index of each encountered element
2128
+ if ( useCache ) {
2129
+ (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
2130
+ }
2131
+
2132
+ if ( node === elem ) {
2133
+ break;
2134
+ }
2135
+ }
2136
+ }
2137
+ }
2138
+
2139
+ // Incorporate the offset, then check against cycle size
2140
+ diff -= last;
2141
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
2142
+ }
2143
+ };
2144
+ },
2145
+
2146
+ "PSEUDO": function( pseudo, argument ) {
2147
+ // pseudo-class names are case-insensitive
2148
+ // http://www.w3.org/TR/selectors/#pseudo-classes
2149
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
2150
+ // Remember that setFilters inherits from pseudos
2151
+ var args,
2152
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
2153
+ Sizzle.error( "unsupported pseudo: " + pseudo );
2154
+
2155
+ // The user may use createPseudo to indicate that
2156
+ // arguments are needed to create the filter function
2157
+ // just as Sizzle does
2158
+ if ( fn[ expando ] ) {
2159
+ return fn( argument );
2160
+ }
2161
+
2162
+ // But maintain support for old signatures
2163
+ if ( fn.length > 1 ) {
2164
+ args = [ pseudo, pseudo, "", argument ];
2165
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
2166
+ markFunction(function( seed, matches ) {
2167
+ var idx,
2168
+ matched = fn( seed, argument ),
2169
+ i = matched.length;
2170
+ while ( i-- ) {
2171
+ idx = indexOf.call( seed, matched[i] );
2172
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
2173
+ }
2174
+ }) :
2175
+ function( elem ) {
2176
+ return fn( elem, 0, args );
2177
+ };
2178
+ }
2179
+
2180
+ return fn;
2181
+ }
2182
+ },
2183
+
2184
+ pseudos: {
2185
+ // Potentially complex pseudos
2186
+ "not": markFunction(function( selector ) {
2187
+ // Trim the selector passed to compile
2188
+ // to avoid treating leading and trailing
2189
+ // spaces as combinators
2190
+ var input = [],
2191
+ results = [],
2192
+ matcher = compile( selector.replace( rtrim, "$1" ) );
2193
+
2194
+ return matcher[ expando ] ?
2195
+ markFunction(function( seed, matches, context, xml ) {
2196
+ var elem,
2197
+ unmatched = matcher( seed, null, xml, [] ),
2198
+ i = seed.length;
2199
+
2200
+ // Match elements unmatched by `matcher`
2201
+ while ( i-- ) {
2202
+ if ( (elem = unmatched[i]) ) {
2203
+ seed[i] = !(matches[i] = elem);
2204
+ }
2205
+ }
2206
+ }) :
2207
+ function( elem, context, xml ) {
2208
+ input[0] = elem;
2209
+ matcher( input, null, xml, results );
2210
+ return !results.pop();
2211
+ };
2212
+ }),
2213
+
2214
+ "has": markFunction(function( selector ) {
2215
+ return function( elem ) {
2216
+ return Sizzle( selector, elem ).length > 0;
2217
+ };
2218
+ }),
2219
+
2220
+ "contains": markFunction(function( text ) {
2221
+ return function( elem ) {
2222
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
2223
+ };
2224
+ }),
2225
+
2226
+ // "Whether an element is represented by a :lang() selector
2227
+ // is based solely on the element's language value
2228
+ // being equal to the identifier C,
2229
+ // or beginning with the identifier C immediately followed by "-".
2230
+ // The matching of C against the element's language value is performed case-insensitively.
2231
+ // The identifier C does not have to be a valid language name."
2232
+ // http://www.w3.org/TR/selectors/#lang-pseudo
2233
+ "lang": markFunction( function( lang ) {
2234
+ // lang value must be a valid identifier
2235
+ if ( !ridentifier.test(lang || "") ) {
2236
+ Sizzle.error( "unsupported lang: " + lang );
2237
+ }
2238
+ lang = lang.replace( runescape, funescape ).toLowerCase();
2239
+ return function( elem ) {
2240
+ var elemLang;
2241
+ do {
2242
+ if ( (elemLang = documentIsHTML ?
2243
+ elem.lang :
2244
+ elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
2245
+
2246
+ elemLang = elemLang.toLowerCase();
2247
+ return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
2248
+ }
2249
+ } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
2250
+ return false;
2251
+ };
2252
+ }),
2253
+
2254
+ // Miscellaneous
2255
+ "target": function( elem ) {
2256
+ var hash = window.location && window.location.hash;
2257
+ return hash && hash.slice( 1 ) === elem.id;
2258
+ },
2259
+
2260
+ "root": function( elem ) {
2261
+ return elem === docElem;
2262
+ },
2263
+
2264
+ "focus": function( elem ) {
2265
+ return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
2266
+ },
2267
+
2268
+ // Boolean properties
2269
+ "enabled": function( elem ) {
2270
+ return elem.disabled === false;
2271
+ },
2272
+
2273
+ "disabled": function( elem ) {
2274
+ return elem.disabled === true;
2275
+ },
2276
+
2277
+ "checked": function( elem ) {
2278
+ // In CSS3, :checked should return both checked and selected elements
2279
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
2280
+ var nodeName = elem.nodeName.toLowerCase();
2281
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
2282
+ },
2283
+
2284
+ "selected": function( elem ) {
2285
+ // Accessing this property makes selected-by-default
2286
+ // options in Safari work properly
2287
+ if ( elem.parentNode ) {
2288
+ elem.parentNode.selectedIndex;
2289
+ }
2290
+
2291
+ return elem.selected === true;
2292
+ },
2293
+
2294
+ // Contents
2295
+ "empty": function( elem ) {
2296
+ // http://www.w3.org/TR/selectors/#empty-pseudo
2297
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
2298
+ // not comment, processing instructions, or others
2299
+ // Thanks to Diego Perini for the nodeName shortcut
2300
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
2301
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
2302
+ if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
2303
+ return false;
2304
+ }
2305
+ }
2306
+ return true;
2307
+ },
2308
+
2309
+ "parent": function( elem ) {
2310
+ return !Expr.pseudos["empty"]( elem );
2311
+ },
2312
+
2313
+ // Element/input types
2314
+ "header": function( elem ) {
2315
+ return rheader.test( elem.nodeName );
2316
+ },
2317
+
2318
+ "input": function( elem ) {
2319
+ return rinputs.test( elem.nodeName );
2320
+ },
2321
+
2322
+ "button": function( elem ) {
2323
+ var name = elem.nodeName.toLowerCase();
2324
+ return name === "input" && elem.type === "button" || name === "button";
2325
+ },
2326
+
2327
+ "text": function( elem ) {
2328
+ var attr;
2329
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
2330
+ // use getAttribute instead to test this case
2331
+ return elem.nodeName.toLowerCase() === "input" &&
2332
+ elem.type === "text" &&
2333
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
2334
+ },
2335
+
2336
+ // Position-in-collection
2337
+ "first": createPositionalPseudo(function() {
2338
+ return [ 0 ];
2339
+ }),
2340
+
2341
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
2342
+ return [ length - 1 ];
2343
+ }),
2344
+
2345
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
2346
+ return [ argument < 0 ? argument + length : argument ];
2347
+ }),
2348
+
2349
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
2350
+ var i = 0;
2351
+ for ( ; i < length; i += 2 ) {
2352
+ matchIndexes.push( i );
2353
+ }
2354
+ return matchIndexes;
2355
+ }),
2356
+
2357
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
2358
+ var i = 1;
2359
+ for ( ; i < length; i += 2 ) {
2360
+ matchIndexes.push( i );
2361
+ }
2362
+ return matchIndexes;
2363
+ }),
2364
+
2365
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
2366
+ var i = argument < 0 ? argument + length : argument;
2367
+ for ( ; --i >= 0; ) {
2368
+ matchIndexes.push( i );
2369
+ }
2370
+ return matchIndexes;
2371
+ }),
2372
+
2373
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
2374
+ var i = argument < 0 ? argument + length : argument;
2375
+ for ( ; ++i < length; ) {
2376
+ matchIndexes.push( i );
2377
+ }
2378
+ return matchIndexes;
2379
+ })
2380
+ }
2381
+ };
2382
+
2383
+ Expr.pseudos["nth"] = Expr.pseudos["eq"];
2384
+
2385
+ // Add button/input type pseudos
2386
+ for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
2387
+ Expr.pseudos[ i ] = createInputPseudo( i );
2388
+ }
2389
+ for ( i in { submit: true, reset: true } ) {
2390
+ Expr.pseudos[ i ] = createButtonPseudo( i );
2391
+ }
2392
+
2393
+ // Easy API for creating new setFilters
2394
+ function setFilters() {}
2395
+ setFilters.prototype = Expr.filters = Expr.pseudos;
2396
+ Expr.setFilters = new setFilters();
2397
+
2398
+ function tokenize( selector, parseOnly ) {
2399
+ var matched, match, tokens, type,
2400
+ soFar, groups, preFilters,
2401
+ cached = tokenCache[ selector + " " ];
2402
+
2403
+ if ( cached ) {
2404
+ return parseOnly ? 0 : cached.slice( 0 );
2405
+ }
2406
+
2407
+ soFar = selector;
2408
+ groups = [];
2409
+ preFilters = Expr.preFilter;
2410
+
2411
+ while ( soFar ) {
2412
+
2413
+ // Comma and first run
2414
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
2415
+ if ( match ) {
2416
+ // Don't consume trailing commas as valid
2417
+ soFar = soFar.slice( match[0].length ) || soFar;
2418
+ }
2419
+ groups.push( tokens = [] );
2420
+ }
2421
+
2422
+ matched = false;
2423
+
2424
+ // Combinators
2425
+ if ( (match = rcombinators.exec( soFar )) ) {
2426
+ matched = match.shift();
2427
+ tokens.push({
2428
+ value: matched,
2429
+ // Cast descendant combinators to space
2430
+ type: match[0].replace( rtrim, " " )
2431
+ });
2432
+ soFar = soFar.slice( matched.length );
2433
+ }
2434
+
2435
+ // Filters
2436
+ for ( type in Expr.filter ) {
2437
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
2438
+ (match = preFilters[ type ]( match ))) ) {
2439
+ matched = match.shift();
2440
+ tokens.push({
2441
+ value: matched,
2442
+ type: type,
2443
+ matches: match
2444
+ });
2445
+ soFar = soFar.slice( matched.length );
2446
+ }
2447
+ }
2448
+
2449
+ if ( !matched ) {
2450
+ break;
2451
+ }
2452
+ }
2453
+
2454
+ // Return the length of the invalid excess
2455
+ // if we're just parsing
2456
+ // Otherwise, throw an error or return tokens
2457
+ return parseOnly ?
2458
+ soFar.length :
2459
+ soFar ?
2460
+ Sizzle.error( selector ) :
2461
+ // Cache the tokens
2462
+ tokenCache( selector, groups ).slice( 0 );
2463
+ }
2464
+
2465
+ function toSelector( tokens ) {
2466
+ var i = 0,
2467
+ len = tokens.length,
2468
+ selector = "";
2469
+ for ( ; i < len; i++ ) {
2470
+ selector += tokens[i].value;
2471
+ }
2472
+ return selector;
2473
+ }
2474
+
2475
+ function addCombinator( matcher, combinator, base ) {
2476
+ var dir = combinator.dir,
2477
+ checkNonElements = base && dir === "parentNode",
2478
+ doneName = done++;
2479
+
2480
+ return combinator.first ?
2481
+ // Check against closest ancestor/preceding element
2482
+ function( elem, context, xml ) {
2483
+ while ( (elem = elem[ dir ]) ) {
2484
+ if ( elem.nodeType === 1 || checkNonElements ) {
2485
+ return matcher( elem, context, xml );
2486
+ }
2487
+ }
2488
+ } :
2489
+
2490
+ // Check against all ancestor/preceding elements
2491
+ function( elem, context, xml ) {
2492
+ var data, cache, outerCache,
2493
+ dirkey = dirruns + " " + doneName;
2494
+
2495
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
2496
+ if ( xml ) {
2497
+ while ( (elem = elem[ dir ]) ) {
2498
+ if ( elem.nodeType === 1 || checkNonElements ) {
2499
+ if ( matcher( elem, context, xml ) ) {
2500
+ return true;
2501
+ }
2502
+ }
2503
+ }
2504
+ } else {
2505
+ while ( (elem = elem[ dir ]) ) {
2506
+ if ( elem.nodeType === 1 || checkNonElements ) {
2507
+ outerCache = elem[ expando ] || (elem[ expando ] = {});
2508
+ if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
2509
+ if ( (data = cache[1]) === true || data === cachedruns ) {
2510
+ return data === true;
2511
+ }
2512
+ } else {
2513
+ cache = outerCache[ dir ] = [ dirkey ];
2514
+ cache[1] = matcher( elem, context, xml ) || cachedruns;
2515
+ if ( cache[1] === true ) {
2516
+ return true;
2517
+ }
2518
+ }
2519
+ }
2520
+ }
2521
+ }
2522
+ };
2523
+ }
2524
+
2525
+ function elementMatcher( matchers ) {
2526
+ return matchers.length > 1 ?
2527
+ function( elem, context, xml ) {
2528
+ var i = matchers.length;
2529
+ while ( i-- ) {
2530
+ if ( !matchers[i]( elem, context, xml ) ) {
2531
+ return false;
2532
+ }
2533
+ }
2534
+ return true;
2535
+ } :
2536
+ matchers[0];
2537
+ }
2538
+
2539
+ function condense( unmatched, map, filter, context, xml ) {
2540
+ var elem,
2541
+ newUnmatched = [],
2542
+ i = 0,
2543
+ len = unmatched.length,
2544
+ mapped = map != null;
2545
+
2546
+ for ( ; i < len; i++ ) {
2547
+ if ( (elem = unmatched[i]) ) {
2548
+ if ( !filter || filter( elem, context, xml ) ) {
2549
+ newUnmatched.push( elem );
2550
+ if ( mapped ) {
2551
+ map.push( i );
2552
+ }
2553
+ }
2554
+ }
2555
+ }
2556
+
2557
+ return newUnmatched;
2558
+ }
2559
+
2560
+ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
2561
+ if ( postFilter && !postFilter[ expando ] ) {
2562
+ postFilter = setMatcher( postFilter );
2563
+ }
2564
+ if ( postFinder && !postFinder[ expando ] ) {
2565
+ postFinder = setMatcher( postFinder, postSelector );
2566
+ }
2567
+ return markFunction(function( seed, results, context, xml ) {
2568
+ var temp, i, elem,
2569
+ preMap = [],
2570
+ postMap = [],
2571
+ preexisting = results.length,
2572
+
2573
+ // Get initial elements from seed or context
2574
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
2575
+
2576
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
2577
+ matcherIn = preFilter && ( seed || !selector ) ?
2578
+ condense( elems, preMap, preFilter, context, xml ) :
2579
+ elems,
2580
+
2581
+ matcherOut = matcher ?
2582
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
2583
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
2584
+
2585
+ // ...intermediate processing is necessary
2586
+ [] :
2587
+
2588
+ // ...otherwise use results directly
2589
+ results :
2590
+ matcherIn;
2591
+
2592
+ // Find primary matches
2593
+ if ( matcher ) {
2594
+ matcher( matcherIn, matcherOut, context, xml );
2595
+ }
2596
+
2597
+ // Apply postFilter
2598
+ if ( postFilter ) {
2599
+ temp = condense( matcherOut, postMap );
2600
+ postFilter( temp, [], context, xml );
2601
+
2602
+ // Un-match failing elements by moving them back to matcherIn
2603
+ i = temp.length;
2604
+ while ( i-- ) {
2605
+ if ( (elem = temp[i]) ) {
2606
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
2607
+ }
2608
+ }
2609
+ }
2610
+
2611
+ if ( seed ) {
2612
+ if ( postFinder || preFilter ) {
2613
+ if ( postFinder ) {
2614
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
2615
+ temp = [];
2616
+ i = matcherOut.length;
2617
+ while ( i-- ) {
2618
+ if ( (elem = matcherOut[i]) ) {
2619
+ // Restore matcherIn since elem is not yet a final match
2620
+ temp.push( (matcherIn[i] = elem) );
2621
+ }
2622
+ }
2623
+ postFinder( null, (matcherOut = []), temp, xml );
2624
+ }
2625
+
2626
+ // Move matched elements from seed to results to keep them synchronized
2627
+ i = matcherOut.length;
2628
+ while ( i-- ) {
2629
+ if ( (elem = matcherOut[i]) &&
2630
+ (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
2631
+
2632
+ seed[temp] = !(results[temp] = elem);
2633
+ }
2634
+ }
2635
+ }
2636
+
2637
+ // Add elements to results, through postFinder if defined
2638
+ } else {
2639
+ matcherOut = condense(
2640
+ matcherOut === results ?
2641
+ matcherOut.splice( preexisting, matcherOut.length ) :
2642
+ matcherOut
2643
+ );
2644
+ if ( postFinder ) {
2645
+ postFinder( null, results, matcherOut, xml );
2646
+ } else {
2647
+ push.apply( results, matcherOut );
2648
+ }
2649
+ }
2650
+ });
2651
+ }
2652
+
2653
+ function matcherFromTokens( tokens ) {
2654
+ var checkContext, matcher, j,
2655
+ len = tokens.length,
2656
+ leadingRelative = Expr.relative[ tokens[0].type ],
2657
+ implicitRelative = leadingRelative || Expr.relative[" "],
2658
+ i = leadingRelative ? 1 : 0,
2659
+
2660
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
2661
+ matchContext = addCombinator( function( elem ) {
2662
+ return elem === checkContext;
2663
+ }, implicitRelative, true ),
2664
+ matchAnyContext = addCombinator( function( elem ) {
2665
+ return indexOf.call( checkContext, elem ) > -1;
2666
+ }, implicitRelative, true ),
2667
+ matchers = [ function( elem, context, xml ) {
2668
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
2669
+ (checkContext = context).nodeType ?
2670
+ matchContext( elem, context, xml ) :
2671
+ matchAnyContext( elem, context, xml ) );
2672
+ } ];
2673
+
2674
+ for ( ; i < len; i++ ) {
2675
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
2676
+ matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
2677
+ } else {
2678
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
2679
+
2680
+ // Return special upon seeing a positional matcher
2681
+ if ( matcher[ expando ] ) {
2682
+ // Find the next relative operator (if any) for proper handling
2683
+ j = ++i;
2684
+ for ( ; j < len; j++ ) {
2685
+ if ( Expr.relative[ tokens[j].type ] ) {
2686
+ break;
2687
+ }
2688
+ }
2689
+ return setMatcher(
2690
+ i > 1 && elementMatcher( matchers ),
2691
+ i > 1 && toSelector(
2692
+ // If the preceding token was a descendant combinator, insert an implicit any-element `*`
2693
+ tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
2694
+ ).replace( rtrim, "$1" ),
2695
+ matcher,
2696
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
2697
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
2698
+ j < len && toSelector( tokens )
2699
+ );
2700
+ }
2701
+ matchers.push( matcher );
2702
+ }
2703
+ }
2704
+
2705
+ return elementMatcher( matchers );
2706
+ }
2707
+
2708
+ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
2709
+ // A counter to specify which element is currently being matched
2710
+ var matcherCachedRuns = 0,
2711
+ bySet = setMatchers.length > 0,
2712
+ byElement = elementMatchers.length > 0,
2713
+ superMatcher = function( seed, context, xml, results, expandContext ) {
2714
+ var elem, j, matcher,
2715
+ setMatched = [],
2716
+ matchedCount = 0,
2717
+ i = "0",
2718
+ unmatched = seed && [],
2719
+ outermost = expandContext != null,
2720
+ contextBackup = outermostContext,
2721
+ // We must always have either seed elements or context
2722
+ elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
2723
+ // Use integer dirruns iff this is the outermost matcher
2724
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
2725
+
2726
+ if ( outermost ) {
2727
+ outermostContext = context !== document && context;
2728
+ cachedruns = matcherCachedRuns;
2729
+ }
2730
+
2731
+ // Add elements passing elementMatchers directly to results
2732
+ // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
2733
+ for ( ; (elem = elems[i]) != null; i++ ) {
2734
+ if ( byElement && elem ) {
2735
+ j = 0;
2736
+ while ( (matcher = elementMatchers[j++]) ) {
2737
+ if ( matcher( elem, context, xml ) ) {
2738
+ results.push( elem );
2739
+ break;
2740
+ }
2741
+ }
2742
+ if ( outermost ) {
2743
+ dirruns = dirrunsUnique;
2744
+ cachedruns = ++matcherCachedRuns;
2745
+ }
2746
+ }
2747
+
2748
+ // Track unmatched elements for set filters
2749
+ if ( bySet ) {
2750
+ // They will have gone through all possible matchers
2751
+ if ( (elem = !matcher && elem) ) {
2752
+ matchedCount--;
2753
+ }
2754
+
2755
+ // Lengthen the array for every element, matched or not
2756
+ if ( seed ) {
2757
+ unmatched.push( elem );
2758
+ }
2759
+ }
2760
+ }
2761
+
2762
+ // Apply set filters to unmatched elements
2763
+ matchedCount += i;
2764
+ if ( bySet && i !== matchedCount ) {
2765
+ j = 0;
2766
+ while ( (matcher = setMatchers[j++]) ) {
2767
+ matcher( unmatched, setMatched, context, xml );
2768
+ }
2769
+
2770
+ if ( seed ) {
2771
+ // Reintegrate element matches to eliminate the need for sorting
2772
+ if ( matchedCount > 0 ) {
2773
+ while ( i-- ) {
2774
+ if ( !(unmatched[i] || setMatched[i]) ) {
2775
+ setMatched[i] = pop.call( results );
2776
+ }
2777
+ }
2778
+ }
2779
+
2780
+ // Discard index placeholder values to get only actual matches
2781
+ setMatched = condense( setMatched );
2782
+ }
2783
+
2784
+ // Add matches to results
2785
+ push.apply( results, setMatched );
2786
+
2787
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
2788
+ if ( outermost && !seed && setMatched.length > 0 &&
2789
+ ( matchedCount + setMatchers.length ) > 1 ) {
2790
+
2791
+ Sizzle.uniqueSort( results );
2792
+ }
2793
+ }
2794
+
2795
+ // Override manipulation of globals by nested matchers
2796
+ if ( outermost ) {
2797
+ dirruns = dirrunsUnique;
2798
+ outermostContext = contextBackup;
2799
+ }
2800
+
2801
+ return unmatched;
2802
+ };
2803
+
2804
+ return bySet ?
2805
+ markFunction( superMatcher ) :
2806
+ superMatcher;
2807
+ }
2808
+
2809
+ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
2810
+ var i,
2811
+ setMatchers = [],
2812
+ elementMatchers = [],
2813
+ cached = compilerCache[ selector + " " ];
2814
+
2815
+ if ( !cached ) {
2816
+ // Generate a function of recursive functions that can be used to check each element
2817
+ if ( !group ) {
2818
+ group = tokenize( selector );
2819
+ }
2820
+ i = group.length;
2821
+ while ( i-- ) {
2822
+ cached = matcherFromTokens( group[i] );
2823
+ if ( cached[ expando ] ) {
2824
+ setMatchers.push( cached );
2825
+ } else {
2826
+ elementMatchers.push( cached );
2827
+ }
2828
+ }
2829
+
2830
+ // Cache the compiled function
2831
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
2832
+ }
2833
+ return cached;
2834
+ };
2835
+
2836
+ function multipleContexts( selector, contexts, results ) {
2837
+ var i = 0,
2838
+ len = contexts.length;
2839
+ for ( ; i < len; i++ ) {
2840
+ Sizzle( selector, contexts[i], results );
2841
+ }
2842
+ return results;
2843
+ }
2844
+
2845
+ function select( selector, context, results, seed ) {
2846
+ var i, tokens, token, type, find,
2847
+ match = tokenize( selector );
2848
+
2849
+ if ( !seed ) {
2850
+ // Try to minimize operations if there is only one group
2851
+ if ( match.length === 1 ) {
2852
+
2853
+ // Take a shortcut and set the context if the root selector is an ID
2854
+ tokens = match[0] = match[0].slice( 0 );
2855
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
2856
+ support.getById && context.nodeType === 9 && documentIsHTML &&
2857
+ Expr.relative[ tokens[1].type ] ) {
2858
+
2859
+ context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
2860
+ if ( !context ) {
2861
+ return results;
2862
+ }
2863
+ selector = selector.slice( tokens.shift().value.length );
2864
+ }
2865
+
2866
+ // Fetch a seed set for right-to-left matching
2867
+ i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
2868
+ while ( i-- ) {
2869
+ token = tokens[i];
2870
+
2871
+ // Abort if we hit a combinator
2872
+ if ( Expr.relative[ (type = token.type) ] ) {
2873
+ break;
2874
+ }
2875
+ if ( (find = Expr.find[ type ]) ) {
2876
+ // Search, expanding context for leading sibling combinators
2877
+ if ( (seed = find(
2878
+ token.matches[0].replace( runescape, funescape ),
2879
+ rsibling.test( tokens[0].type ) && context.parentNode || context
2880
+ )) ) {
2881
+
2882
+ // If seed is empty or no tokens remain, we can return early
2883
+ tokens.splice( i, 1 );
2884
+ selector = seed.length && toSelector( tokens );
2885
+ if ( !selector ) {
2886
+ push.apply( results, seed );
2887
+ return results;
2888
+ }
2889
+
2890
+ break;
2891
+ }
2892
+ }
2893
+ }
2894
+ }
2895
+ }
2896
+
2897
+ // Compile and execute a filtering function
2898
+ // Provide `match` to avoid retokenization if we modified the selector above
2899
+ compile( selector, match )(
2900
+ seed,
2901
+ context,
2902
+ !documentIsHTML,
2903
+ results,
2904
+ rsibling.test( selector )
2905
+ );
2906
+ return results;
2907
+ }
2908
+
2909
+ // One-time assignments
2910
+
2911
+ // Sort stability
2912
+ support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
2913
+
2914
+ // Support: Chrome<14
2915
+ // Always assume duplicates if they aren't passed to the comparison function
2916
+ support.detectDuplicates = hasDuplicate;
2917
+
2918
+ // Initialize against the default document
2919
+ setDocument();
2920
+
2921
+ // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
2922
+ // Detached nodes confoundingly follow *each other*
2923
+ support.sortDetached = assert(function( div1 ) {
2924
+ // Should return 1, but returns 4 (following)
2925
+ return div1.compareDocumentPosition( document.createElement("div") ) & 1;
2926
+ });
2927
+
2928
+ // Support: IE<8
2929
+ // Prevent attribute/property "interpolation"
2930
+ // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
2931
+ if ( !assert(function( div ) {
2932
+ div.innerHTML = "<a href='#'></a>";
2933
+ return div.firstChild.getAttribute("href") === "#" ;
2934
+ }) ) {
2935
+ addHandle( "type|href|height|width", function( elem, name, isXML ) {
2936
+ if ( !isXML ) {
2937
+ return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
2938
+ }
2939
+ });
2940
+ }
2941
+
2942
+ // Support: IE<9
2943
+ // Use defaultValue in place of getAttribute("value")
2944
+ if ( !support.attributes || !assert(function( div ) {
2945
+ div.innerHTML = "<input/>";
2946
+ div.firstChild.setAttribute( "value", "" );
2947
+ return div.firstChild.getAttribute( "value" ) === "";
2948
+ }) ) {
2949
+ addHandle( "value", function( elem, name, isXML ) {
2950
+ if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
2951
+ return elem.defaultValue;
2952
+ }
2953
+ });
2954
+ }
2955
+
2956
+ // Support: IE<9
2957
+ // Use getAttributeNode to fetch booleans when getAttribute lies
2958
+ if ( !assert(function( div ) {
2959
+ return div.getAttribute("disabled") == null;
2960
+ }) ) {
2961
+ addHandle( booleans, function( elem, name, isXML ) {
2962
+ var val;
2963
+ if ( !isXML ) {
2964
+ return (val = elem.getAttributeNode( name )) && val.specified ?
2965
+ val.value :
2966
+ elem[ name ] === true ? name.toLowerCase() : null;
2967
+ }
2968
+ });
2969
+ }
2970
+
2971
+ jQuery.find = Sizzle;
2972
+ jQuery.expr = Sizzle.selectors;
2973
+ jQuery.expr[":"] = jQuery.expr.pseudos;
2974
+ jQuery.unique = Sizzle.uniqueSort;
2975
+ jQuery.text = Sizzle.getText;
2976
+ jQuery.isXMLDoc = Sizzle.isXML;
2977
+ jQuery.contains = Sizzle.contains;
2978
+
2979
+
2980
+ })( window );
2981
+ // String to Object options format cache
2982
+ var optionsCache = {};
2983
+
2984
+ // Convert String-formatted options into Object-formatted ones and store in cache
2985
+ function createOptions( options ) {
2986
+ var object = optionsCache[ options ] = {};
2987
+ jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
2988
+ object[ flag ] = true;
2989
+ });
2990
+ return object;
2991
+ }
2992
+
2993
+ /*
2994
+ * Create a callback list using the following parameters:
2995
+ *
2996
+ * options: an optional list of space-separated options that will change how
2997
+ * the callback list behaves or a more traditional option object
2998
+ *
2999
+ * By default a callback list will act like an event callback list and can be
3000
+ * "fired" multiple times.
3001
+ *
3002
+ * Possible options:
3003
+ *
3004
+ * once: will ensure the callback list can only be fired once (like a Deferred)
3005
+ *
3006
+ * memory: will keep track of previous values and will call any callback added
3007
+ * after the list has been fired right away with the latest "memorized"
3008
+ * values (like a Deferred)
3009
+ *
3010
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
3011
+ *
3012
+ * stopOnFalse: interrupt callings when a callback returns false
3013
+ *
3014
+ */
3015
+ jQuery.Callbacks = function( options ) {
3016
+
3017
+ // Convert options from String-formatted to Object-formatted if needed
3018
+ // (we check in cache first)
3019
+ options = typeof options === "string" ?
3020
+ ( optionsCache[ options ] || createOptions( options ) ) :
3021
+ jQuery.extend( {}, options );
3022
+
3023
+ var // Flag to know if list is currently firing
3024
+ firing,
3025
+ // Last fire value (for non-forgettable lists)
3026
+ memory,
3027
+ // Flag to know if list was already fired
3028
+ fired,
3029
+ // End of the loop when firing
3030
+ firingLength,
3031
+ // Index of currently firing callback (modified by remove if needed)
3032
+ firingIndex,
3033
+ // First callback to fire (used internally by add and fireWith)
3034
+ firingStart,
3035
+ // Actual callback list
3036
+ list = [],
3037
+ // Stack of fire calls for repeatable lists
3038
+ stack = !options.once && [],
3039
+ // Fire callbacks
3040
+ fire = function( data ) {
3041
+ memory = options.memory && data;
3042
+ fired = true;
3043
+ firingIndex = firingStart || 0;
3044
+ firingStart = 0;
3045
+ firingLength = list.length;
3046
+ firing = true;
3047
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
3048
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
3049
+ memory = false; // To prevent further calls using add
3050
+ break;
3051
+ }
3052
+ }
3053
+ firing = false;
3054
+ if ( list ) {
3055
+ if ( stack ) {
3056
+ if ( stack.length ) {
3057
+ fire( stack.shift() );
3058
+ }
3059
+ } else if ( memory ) {
3060
+ list = [];
3061
+ } else {
3062
+ self.disable();
3063
+ }
3064
+ }
3065
+ },
3066
+ // Actual Callbacks object
3067
+ self = {
3068
+ // Add a callback or a collection of callbacks to the list
3069
+ add: function() {
3070
+ if ( list ) {
3071
+ // First, we save the current length
3072
+ var start = list.length;
3073
+ (function add( args ) {
3074
+ jQuery.each( args, function( _, arg ) {
3075
+ var type = jQuery.type( arg );
3076
+ if ( type === "function" ) {
3077
+ if ( !options.unique || !self.has( arg ) ) {
3078
+ list.push( arg );
3079
+ }
3080
+ } else if ( arg && arg.length && type !== "string" ) {
3081
+ // Inspect recursively
3082
+ add( arg );
3083
+ }
3084
+ });
3085
+ })( arguments );
3086
+ // Do we need to add the callbacks to the
3087
+ // current firing batch?
3088
+ if ( firing ) {
3089
+ firingLength = list.length;
3090
+ // With memory, if we're not firing then
3091
+ // we should call right away
3092
+ } else if ( memory ) {
3093
+ firingStart = start;
3094
+ fire( memory );
3095
+ }
3096
+ }
3097
+ return this;
3098
+ },
3099
+ // Remove a callback from the list
3100
+ remove: function() {
3101
+ if ( list ) {
3102
+ jQuery.each( arguments, function( _, arg ) {
3103
+ var index;
3104
+ while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
3105
+ list.splice( index, 1 );
3106
+ // Handle firing indexes
3107
+ if ( firing ) {
3108
+ if ( index <= firingLength ) {
3109
+ firingLength--;
3110
+ }
3111
+ if ( index <= firingIndex ) {
3112
+ firingIndex--;
3113
+ }
3114
+ }
3115
+ }
3116
+ });
3117
+ }
3118
+ return this;
3119
+ },
3120
+ // Check if a given callback is in the list.
3121
+ // If no argument is given, return whether or not list has callbacks attached.
3122
+ has: function( fn ) {
3123
+ return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
3124
+ },
3125
+ // Remove all callbacks from the list
3126
+ empty: function() {
3127
+ list = [];
3128
+ firingLength = 0;
3129
+ return this;
3130
+ },
3131
+ // Have the list do nothing anymore
3132
+ disable: function() {
3133
+ list = stack = memory = undefined;
3134
+ return this;
3135
+ },
3136
+ // Is it disabled?
3137
+ disabled: function() {
3138
+ return !list;
3139
+ },
3140
+ // Lock the list in its current state
3141
+ lock: function() {
3142
+ stack = undefined;
3143
+ if ( !memory ) {
3144
+ self.disable();
3145
+ }
3146
+ return this;
3147
+ },
3148
+ // Is it locked?
3149
+ locked: function() {
3150
+ return !stack;
3151
+ },
3152
+ // Call all callbacks with the given context and arguments
3153
+ fireWith: function( context, args ) {
3154
+ if ( list && ( !fired || stack ) ) {
3155
+ args = args || [];
3156
+ args = [ context, args.slice ? args.slice() : args ];
3157
+ if ( firing ) {
3158
+ stack.push( args );
3159
+ } else {
3160
+ fire( args );
3161
+ }
3162
+ }
3163
+ return this;
3164
+ },
3165
+ // Call all the callbacks with the given arguments
3166
+ fire: function() {
3167
+ self.fireWith( this, arguments );
3168
+ return this;
3169
+ },
3170
+ // To know if the callbacks have already been called at least once
3171
+ fired: function() {
3172
+ return !!fired;
3173
+ }
3174
+ };
3175
+
3176
+ return self;
3177
+ };
3178
+ jQuery.extend({
3179
+
3180
+ Deferred: function( func ) {
3181
+ var tuples = [
3182
+ // action, add listener, listener list, final state
3183
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
3184
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
3185
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
3186
+ ],
3187
+ state = "pending",
3188
+ promise = {
3189
+ state: function() {
3190
+ return state;
3191
+ },
3192
+ always: function() {
3193
+ deferred.done( arguments ).fail( arguments );
3194
+ return this;
3195
+ },
3196
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
3197
+ var fns = arguments;
3198
+ return jQuery.Deferred(function( newDefer ) {
3199
+ jQuery.each( tuples, function( i, tuple ) {
3200
+ var action = tuple[ 0 ],
3201
+ fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
3202
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
3203
+ deferred[ tuple[1] ](function() {
3204
+ var returned = fn && fn.apply( this, arguments );
3205
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
3206
+ returned.promise()
3207
+ .done( newDefer.resolve )
3208
+ .fail( newDefer.reject )
3209
+ .progress( newDefer.notify );
3210
+ } else {
3211
+ newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
3212
+ }
3213
+ });
3214
+ });
3215
+ fns = null;
3216
+ }).promise();
3217
+ },
3218
+ // Get a promise for this deferred
3219
+ // If obj is provided, the promise aspect is added to the object
3220
+ promise: function( obj ) {
3221
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
3222
+ }
3223
+ },
3224
+ deferred = {};
3225
+
3226
+ // Keep pipe for back-compat
3227
+ promise.pipe = promise.then;
3228
+
3229
+ // Add list-specific methods
3230
+ jQuery.each( tuples, function( i, tuple ) {
3231
+ var list = tuple[ 2 ],
3232
+ stateString = tuple[ 3 ];
3233
+
3234
+ // promise[ done | fail | progress ] = list.add
3235
+ promise[ tuple[1] ] = list.add;
3236
+
3237
+ // Handle state
3238
+ if ( stateString ) {
3239
+ list.add(function() {
3240
+ // state = [ resolved | rejected ]
3241
+ state = stateString;
3242
+
3243
+ // [ reject_list | resolve_list ].disable; progress_list.lock
3244
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
3245
+ }
3246
+
3247
+ // deferred[ resolve | reject | notify ]
3248
+ deferred[ tuple[0] ] = function() {
3249
+ deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
3250
+ return this;
3251
+ };
3252
+ deferred[ tuple[0] + "With" ] = list.fireWith;
3253
+ });
3254
+
3255
+ // Make the deferred a promise
3256
+ promise.promise( deferred );
3257
+
3258
+ // Call given func if any
3259
+ if ( func ) {
3260
+ func.call( deferred, deferred );
3261
+ }
3262
+
3263
+ // All done!
3264
+ return deferred;
3265
+ },
3266
+
3267
+ // Deferred helper
3268
+ when: function( subordinate /* , ..., subordinateN */ ) {
3269
+ var i = 0,
3270
+ resolveValues = core_slice.call( arguments ),
3271
+ length = resolveValues.length,
3272
+
3273
+ // the count of uncompleted subordinates
3274
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
3275
+
3276
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
3277
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
3278
+
3279
+ // Update function for both resolve and progress values
3280
+ updateFunc = function( i, contexts, values ) {
3281
+ return function( value ) {
3282
+ contexts[ i ] = this;
3283
+ values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
3284
+ if( values === progressValues ) {
3285
+ deferred.notifyWith( contexts, values );
3286
+ } else if ( !( --remaining ) ) {
3287
+ deferred.resolveWith( contexts, values );
3288
+ }
3289
+ };
3290
+ },
3291
+
3292
+ progressValues, progressContexts, resolveContexts;
3293
+
3294
+ // add listeners to Deferred subordinates; treat others as resolved
3295
+ if ( length > 1 ) {
3296
+ progressValues = new Array( length );
3297
+ progressContexts = new Array( length );
3298
+ resolveContexts = new Array( length );
3299
+ for ( ; i < length; i++ ) {
3300
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
3301
+ resolveValues[ i ].promise()
3302
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
3303
+ .fail( deferred.reject )
3304
+ .progress( updateFunc( i, progressContexts, progressValues ) );
3305
+ } else {
3306
+ --remaining;
3307
+ }
3308
+ }
3309
+ }
3310
+
3311
+ // if we're not waiting on anything, resolve the master
3312
+ if ( !remaining ) {
3313
+ deferred.resolveWith( resolveContexts, resolveValues );
3314
+ }
3315
+
3316
+ return deferred.promise();
3317
+ }
3318
+ });
3319
+ jQuery.support = (function( support ) {
3320
+
3321
+ var all, a, input, select, fragment, opt, eventName, isSupported, i,
3322
+ div = document.createElement("div");
3323
+
3324
+ // Setup
3325
+ div.setAttribute( "className", "t" );
3326
+ div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
3327
+
3328
+ // Finish early in limited (non-browser) environments
3329
+ all = div.getElementsByTagName("*") || [];
3330
+ a = div.getElementsByTagName("a")[ 0 ];
3331
+ if ( !a || !a.style || !all.length ) {
3332
+ return support;
3333
+ }
3334
+
3335
+ // First batch of tests
3336
+ select = document.createElement("select");
3337
+ opt = select.appendChild( document.createElement("option") );
3338
+ input = div.getElementsByTagName("input")[ 0 ];
3339
+
3340
+ a.style.cssText = "top:1px;float:left;opacity:.5";
3341
+
3342
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
3343
+ support.getSetAttribute = div.className !== "t";
3344
+
3345
+ // IE strips leading whitespace when .innerHTML is used
3346
+ support.leadingWhitespace = div.firstChild.nodeType === 3;
3347
+
3348
+ // Make sure that tbody elements aren't automatically inserted
3349
+ // IE will insert them into empty tables
3350
+ support.tbody = !div.getElementsByTagName("tbody").length;
3351
+
3352
+ // Make sure that link elements get serialized correctly by innerHTML
3353
+ // This requires a wrapper element in IE
3354
+ support.htmlSerialize = !!div.getElementsByTagName("link").length;
3355
+
3356
+ // Get the style information from getAttribute
3357
+ // (IE uses .cssText instead)
3358
+ support.style = /top/.test( a.getAttribute("style") );
3359
+
3360
+ // Make sure that URLs aren't manipulated
3361
+ // (IE normalizes it by default)
3362
+ support.hrefNormalized = a.getAttribute("href") === "/a";
3363
+
3364
+ // Make sure that element opacity exists
3365
+ // (IE uses filter instead)
3366
+ // Use a regex to work around a WebKit issue. See #5145
3367
+ support.opacity = /^0.5/.test( a.style.opacity );
3368
+
3369
+ // Verify style float existence
3370
+ // (IE uses styleFloat instead of cssFloat)
3371
+ support.cssFloat = !!a.style.cssFloat;
3372
+
3373
+ // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
3374
+ support.checkOn = !!input.value;
3375
+
3376
+ // Make sure that a selected-by-default option has a working selected property.
3377
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
3378
+ support.optSelected = opt.selected;
3379
+
3380
+ // Tests for enctype support on a form (#6743)
3381
+ support.enctype = !!document.createElement("form").enctype;
3382
+
3383
+ // Makes sure cloning an html5 element does not cause problems
3384
+ // Where outerHTML is undefined, this still works
3385
+ support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>";
3386
+
3387
+ // Will be defined later
3388
+ support.inlineBlockNeedsLayout = false;
3389
+ support.shrinkWrapBlocks = false;
3390
+ support.pixelPosition = false;
3391
+ support.deleteExpando = true;
3392
+ support.noCloneEvent = true;
3393
+ support.reliableMarginRight = true;
3394
+ support.boxSizingReliable = true;
3395
+
3396
+ // Make sure checked status is properly cloned
3397
+ input.checked = true;
3398
+ support.noCloneChecked = input.cloneNode( true ).checked;
3399
+
3400
+ // Make sure that the options inside disabled selects aren't marked as disabled
3401
+ // (WebKit marks them as disabled)
3402
+ select.disabled = true;
3403
+ support.optDisabled = !opt.disabled;
3404
+
3405
+ // Support: IE<9
3406
+ try {
3407
+ delete div.test;
3408
+ } catch( e ) {
3409
+ support.deleteExpando = false;
3410
+ }
3411
+
3412
+ // Check if we can trust getAttribute("value")
3413
+ input = document.createElement("input");
3414
+ input.setAttribute( "value", "" );
3415
+ support.input = input.getAttribute( "value" ) === "";
3416
+
3417
+ // Check if an input maintains its value after becoming a radio
3418
+ input.value = "t";
3419
+ input.setAttribute( "type", "radio" );
3420
+ support.radioValue = input.value === "t";
3421
+
3422
+ // #11217 - WebKit loses check when the name is after the checked attribute
3423
+ input.setAttribute( "checked", "t" );
3424
+ input.setAttribute( "name", "t" );
3425
+
3426
+ fragment = document.createDocumentFragment();
3427
+ fragment.appendChild( input );
3428
+
3429
+ // Check if a disconnected checkbox will retain its checked
3430
+ // value of true after appended to the DOM (IE6/7)
3431
+ support.appendChecked = input.checked;
3432
+
3433
+ // WebKit doesn't clone checked state correctly in fragments
3434
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
3435
+
3436
+ // Support: IE<9
3437
+ // Opera does not clone events (and typeof div.attachEvent === undefined).
3438
+ // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
3439
+ if ( div.attachEvent ) {
3440
+ div.attachEvent( "onclick", function() {
3441
+ support.noCloneEvent = false;
3442
+ });
3443
+
3444
+ div.cloneNode( true ).click();
3445
+ }
3446
+
3447
+ // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
3448
+ // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
3449
+ for ( i in { submit: true, change: true, focusin: true }) {
3450
+ div.setAttribute( eventName = "on" + i, "t" );
3451
+
3452
+ support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
3453
+ }
3454
+
3455
+ div.style.backgroundClip = "content-box";
3456
+ div.cloneNode( true ).style.backgroundClip = "";
3457
+ support.clearCloneStyle = div.style.backgroundClip === "content-box";
3458
+
3459
+ // Support: IE<9
3460
+ // Iteration over object's inherited properties before its own.
3461
+ for ( i in jQuery( support ) ) {
3462
+ break;
3463
+ }
3464
+ support.ownLast = i !== "0";
3465
+
3466
+ // Run tests that need a body at doc ready
3467
+ jQuery(function() {
3468
+ var container, marginDiv, tds,
3469
+ divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
3470
+ body = document.getElementsByTagName("body")[0];
3471
+
3472
+ if ( !body ) {
3473
+ // Return for frameset docs that don't have a body
3474
+ return;
3475
+ }
3476
+
3477
+ container = document.createElement("div");
3478
+ container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
3479
+
3480
+ body.appendChild( container ).appendChild( div );
3481
+
3482
+ // Support: IE8
3483
+ // Check if table cells still have offsetWidth/Height when they are set
3484
+ // to display:none and there are still other visible table cells in a
3485
+ // table row; if so, offsetWidth/Height are not reliable for use when
3486
+ // determining if an element has been hidden directly using
3487
+ // display:none (it is still safe to use offsets if a parent element is
3488
+ // hidden; don safety goggles and see bug #4512 for more information).
3489
+ div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
3490
+ tds = div.getElementsByTagName("td");
3491
+ tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
3492
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
3493
+
3494
+ tds[ 0 ].style.display = "";
3495
+ tds[ 1 ].style.display = "none";
3496
+
3497
+ // Support: IE8
3498
+ // Check if empty table cells still have offsetWidth/Height
3499
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
3500
+
3501
+ // Check box-sizing and margin behavior.
3502
+ div.innerHTML = "";
3503
+ div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
3504
+
3505
+ // Workaround failing boxSizing test due to offsetWidth returning wrong value
3506
+ // with some non-1 values of body zoom, ticket #13543
3507
+ jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
3508
+ support.boxSizing = div.offsetWidth === 4;
3509
+ });
3510
+
3511
+ // Use window.getComputedStyle because jsdom on node.js will break without it.
3512
+ if ( window.getComputedStyle ) {
3513
+ support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
3514
+ support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
3515
+
3516
+ // Check if div with explicit width and no margin-right incorrectly
3517
+ // gets computed margin-right based on width of container. (#3333)
3518
+ // Fails in WebKit before Feb 2011 nightlies
3519
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
3520
+ marginDiv = div.appendChild( document.createElement("div") );
3521
+ marginDiv.style.cssText = div.style.cssText = divReset;
3522
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
3523
+ div.style.width = "1px";
3524
+
3525
+ support.reliableMarginRight =
3526
+ !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
3527
+ }
3528
+
3529
+ if ( typeof div.style.zoom !== core_strundefined ) {
3530
+ // Support: IE<8
3531
+ // Check if natively block-level elements act like inline-block
3532
+ // elements when setting their display to 'inline' and giving
3533
+ // them layout
3534
+ div.innerHTML = "";
3535
+ div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
3536
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
3537
+
3538
+ // Support: IE6
3539
+ // Check if elements with layout shrink-wrap their children
3540
+ div.style.display = "block";
3541
+ div.innerHTML = "<div></div>";
3542
+ div.firstChild.style.width = "5px";
3543
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
3544
+
3545
+ if ( support.inlineBlockNeedsLayout ) {
3546
+ // Prevent IE 6 from affecting layout for positioned elements #11048
3547
+ // Prevent IE from shrinking the body in IE 7 mode #12869
3548
+ // Support: IE<8
3549
+ body.style.zoom = 1;
3550
+ }
3551
+ }
3552
+
3553
+ body.removeChild( container );
3554
+
3555
+ // Null elements to avoid leaks in IE
3556
+ container = div = tds = marginDiv = null;
3557
+ });
3558
+
3559
+ // Null elements to avoid leaks in IE
3560
+ all = select = fragment = opt = a = input = null;
3561
+
3562
+ return support;
3563
+ })({});
3564
+
3565
+ var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
3566
+ rmultiDash = /([A-Z])/g;
3567
+
3568
+ function internalData( elem, name, data, pvt /* Internal Use Only */ ){
3569
+ if ( !jQuery.acceptData( elem ) ) {
3570
+ return;
3571
+ }
3572
+
3573
+ var ret, thisCache,
3574
+ internalKey = jQuery.expando,
3575
+
3576
+ // We have to handle DOM nodes and JS objects differently because IE6-7
3577
+ // can't GC object references properly across the DOM-JS boundary
3578
+ isNode = elem.nodeType,
3579
+
3580
+ // Only DOM nodes need the global jQuery cache; JS object data is
3581
+ // attached directly to the object so GC can occur automatically
3582
+ cache = isNode ? jQuery.cache : elem,
3583
+
3584
+ // Only defining an ID for JS objects if its cache already exists allows
3585
+ // the code to shortcut on the same path as a DOM node with no cache
3586
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
3587
+
3588
+ // Avoid doing any more work than we need to when trying to get data on an
3589
+ // object that has no data at all
3590
+ if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
3591
+ return;
3592
+ }
3593
+
3594
+ if ( !id ) {
3595
+ // Only DOM nodes need a new unique ID for each element since their data
3596
+ // ends up in the global cache
3597
+ if ( isNode ) {
3598
+ id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++;
3599
+ } else {
3600
+ id = internalKey;
3601
+ }
3602
+ }
3603
+
3604
+ if ( !cache[ id ] ) {
3605
+ // Avoid exposing jQuery metadata on plain JS objects when the object
3606
+ // is serialized using JSON.stringify
3607
+ cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
3608
+ }
3609
+
3610
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
3611
+ // shallow copied over onto the existing cache
3612
+ if ( typeof name === "object" || typeof name === "function" ) {
3613
+ if ( pvt ) {
3614
+ cache[ id ] = jQuery.extend( cache[ id ], name );
3615
+ } else {
3616
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
3617
+ }
3618
+ }
3619
+
3620
+ thisCache = cache[ id ];
3621
+
3622
+ // jQuery data() is stored in a separate object inside the object's internal data
3623
+ // cache in order to avoid key collisions between internal data and user-defined
3624
+ // data.
3625
+ if ( !pvt ) {
3626
+ if ( !thisCache.data ) {
3627
+ thisCache.data = {};
3628
+ }
3629
+
3630
+ thisCache = thisCache.data;
3631
+ }
3632
+
3633
+ if ( data !== undefined ) {
3634
+ thisCache[ jQuery.camelCase( name ) ] = data;
3635
+ }
3636
+
3637
+ // Check for both converted-to-camel and non-converted data property names
3638
+ // If a data property was specified
3639
+ if ( typeof name === "string" ) {
3640
+
3641
+ // First Try to find as-is property data
3642
+ ret = thisCache[ name ];
3643
+
3644
+ // Test for null|undefined property data
3645
+ if ( ret == null ) {
3646
+
3647
+ // Try to find the camelCased property
3648
+ ret = thisCache[ jQuery.camelCase( name ) ];
3649
+ }
3650
+ } else {
3651
+ ret = thisCache;
3652
+ }
3653
+
3654
+ return ret;
3655
+ }
3656
+
3657
+ function internalRemoveData( elem, name, pvt ) {
3658
+ if ( !jQuery.acceptData( elem ) ) {
3659
+ return;
3660
+ }
3661
+
3662
+ var thisCache, i,
3663
+ isNode = elem.nodeType,
3664
+
3665
+ // See jQuery.data for more information
3666
+ cache = isNode ? jQuery.cache : elem,
3667
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
3668
+
3669
+ // If there is already no cache entry for this object, there is no
3670
+ // purpose in continuing
3671
+ if ( !cache[ id ] ) {
3672
+ return;
3673
+ }
3674
+
3675
+ if ( name ) {
3676
+
3677
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
3678
+
3679
+ if ( thisCache ) {
3680
+
3681
+ // Support array or space separated string names for data keys
3682
+ if ( !jQuery.isArray( name ) ) {
3683
+
3684
+ // try the string as a key before any manipulation
3685
+ if ( name in thisCache ) {
3686
+ name = [ name ];
3687
+ } else {
3688
+
3689
+ // split the camel cased version by spaces unless a key with the spaces exists
3690
+ name = jQuery.camelCase( name );
3691
+ if ( name in thisCache ) {
3692
+ name = [ name ];
3693
+ } else {
3694
+ name = name.split(" ");
3695
+ }
3696
+ }
3697
+ } else {
3698
+ // If "name" is an array of keys...
3699
+ // When data is initially created, via ("key", "val") signature,
3700
+ // keys will be converted to camelCase.
3701
+ // Since there is no way to tell _how_ a key was added, remove
3702
+ // both plain key and camelCase key. #12786
3703
+ // This will only penalize the array argument path.
3704
+ name = name.concat( jQuery.map( name, jQuery.camelCase ) );
3705
+ }
3706
+
3707
+ i = name.length;
3708
+ while ( i-- ) {
3709
+ delete thisCache[ name[i] ];
3710
+ }
3711
+
3712
+ // If there is no data left in the cache, we want to continue
3713
+ // and let the cache object itself get destroyed
3714
+ if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
3715
+ return;
3716
+ }
3717
+ }
3718
+ }
3719
+
3720
+ // See jQuery.data for more information
3721
+ if ( !pvt ) {
3722
+ delete cache[ id ].data;
3723
+
3724
+ // Don't destroy the parent cache unless the internal data object
3725
+ // had been the only thing left in it
3726
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
3727
+ return;
3728
+ }
3729
+ }
3730
+
3731
+ // Destroy the cache
3732
+ if ( isNode ) {
3733
+ jQuery.cleanData( [ elem ], true );
3734
+
3735
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
3736
+ /* jshint eqeqeq: false */
3737
+ } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
3738
+ /* jshint eqeqeq: true */
3739
+ delete cache[ id ];
3740
+
3741
+ // When all else fails, null
3742
+ } else {
3743
+ cache[ id ] = null;
3744
+ }
3745
+ }
3746
+
3747
+ jQuery.extend({
3748
+ cache: {},
3749
+
3750
+ // The following elements throw uncatchable exceptions if you
3751
+ // attempt to add expando properties to them.
3752
+ noData: {
3753
+ "applet": true,
3754
+ "embed": true,
3755
+ // Ban all objects except for Flash (which handle expandos)
3756
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
3757
+ },
3758
+
3759
+ hasData: function( elem ) {
3760
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
3761
+ return !!elem && !isEmptyDataObject( elem );
3762
+ },
3763
+
3764
+ data: function( elem, name, data ) {
3765
+ return internalData( elem, name, data );
3766
+ },
3767
+
3768
+ removeData: function( elem, name ) {
3769
+ return internalRemoveData( elem, name );
3770
+ },
3771
+
3772
+ // For internal use only.
3773
+ _data: function( elem, name, data ) {
3774
+ return internalData( elem, name, data, true );
3775
+ },
3776
+
3777
+ _removeData: function( elem, name ) {
3778
+ return internalRemoveData( elem, name, true );
3779
+ },
3780
+
3781
+ // A method for determining if a DOM node can handle the data expando
3782
+ acceptData: function( elem ) {
3783
+ // Do not set data on non-element because it will not be cleared (#8335).
3784
+ if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
3785
+ return false;
3786
+ }
3787
+
3788
+ var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
3789
+
3790
+ // nodes accept data unless otherwise specified; rejection can be conditional
3791
+ return !noData || noData !== true && elem.getAttribute("classid") === noData;
3792
+ }
3793
+ });
3794
+
3795
+ jQuery.fn.extend({
3796
+ data: function( key, value ) {
3797
+ var attrs, name,
3798
+ data = null,
3799
+ i = 0,
3800
+ elem = this[0];
3801
+
3802
+ // Special expections of .data basically thwart jQuery.access,
3803
+ // so implement the relevant behavior ourselves
3804
+
3805
+ // Gets all values
3806
+ if ( key === undefined ) {
3807
+ if ( this.length ) {
3808
+ data = jQuery.data( elem );
3809
+
3810
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
3811
+ attrs = elem.attributes;
3812
+ for ( ; i < attrs.length; i++ ) {
3813
+ name = attrs[i].name;
3814
+
3815
+ if ( name.indexOf("data-") === 0 ) {
3816
+ name = jQuery.camelCase( name.slice(5) );
3817
+
3818
+ dataAttr( elem, name, data[ name ] );
3819
+ }
3820
+ }
3821
+ jQuery._data( elem, "parsedAttrs", true );
3822
+ }
3823
+ }
3824
+
3825
+ return data;
3826
+ }
3827
+
3828
+ // Sets multiple values
3829
+ if ( typeof key === "object" ) {
3830
+ return this.each(function() {
3831
+ jQuery.data( this, key );
3832
+ });
3833
+ }
3834
+
3835
+ return arguments.length > 1 ?
3836
+
3837
+ // Sets one value
3838
+ this.each(function() {
3839
+ jQuery.data( this, key, value );
3840
+ }) :
3841
+
3842
+ // Gets one value
3843
+ // Try to fetch any internally stored data first
3844
+ elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
3845
+ },
3846
+
3847
+ removeData: function( key ) {
3848
+ return this.each(function() {
3849
+ jQuery.removeData( this, key );
3850
+ });
3851
+ }
3852
+ });
3853
+
3854
+ function dataAttr( elem, key, data ) {
3855
+ // If nothing was found internally, try to fetch any
3856
+ // data from the HTML5 data-* attribute
3857
+ if ( data === undefined && elem.nodeType === 1 ) {
3858
+
3859
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
3860
+
3861
+ data = elem.getAttribute( name );
3862
+
3863
+ if ( typeof data === "string" ) {
3864
+ try {
3865
+ data = data === "true" ? true :
3866
+ data === "false" ? false :
3867
+ data === "null" ? null :
3868
+ // Only convert to a number if it doesn't change the string
3869
+ +data + "" === data ? +data :
3870
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
3871
+ data;
3872
+ } catch( e ) {}
3873
+
3874
+ // Make sure we set the data so it isn't changed later
3875
+ jQuery.data( elem, key, data );
3876
+
3877
+ } else {
3878
+ data = undefined;
3879
+ }
3880
+ }
3881
+
3882
+ return data;
3883
+ }
3884
+
3885
+ // checks a cache object for emptiness
3886
+ function isEmptyDataObject( obj ) {
3887
+ var name;
3888
+ for ( name in obj ) {
3889
+
3890
+ // if the public data object is empty, the private is still empty
3891
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
3892
+ continue;
3893
+ }
3894
+ if ( name !== "toJSON" ) {
3895
+ return false;
3896
+ }
3897
+ }
3898
+
3899
+ return true;
3900
+ }
3901
+ jQuery.extend({
3902
+ queue: function( elem, type, data ) {
3903
+ var queue;
3904
+
3905
+ if ( elem ) {
3906
+ type = ( type || "fx" ) + "queue";
3907
+ queue = jQuery._data( elem, type );
3908
+
3909
+ // Speed up dequeue by getting out quickly if this is just a lookup
3910
+ if ( data ) {
3911
+ if ( !queue || jQuery.isArray(data) ) {
3912
+ queue = jQuery._data( elem, type, jQuery.makeArray(data) );
3913
+ } else {
3914
+ queue.push( data );
3915
+ }
3916
+ }
3917
+ return queue || [];
3918
+ }
3919
+ },
3920
+
3921
+ dequeue: function( elem, type ) {
3922
+ type = type || "fx";
3923
+
3924
+ var queue = jQuery.queue( elem, type ),
3925
+ startLength = queue.length,
3926
+ fn = queue.shift(),
3927
+ hooks = jQuery._queueHooks( elem, type ),
3928
+ next = function() {
3929
+ jQuery.dequeue( elem, type );
3930
+ };
3931
+
3932
+ // If the fx queue is dequeued, always remove the progress sentinel
3933
+ if ( fn === "inprogress" ) {
3934
+ fn = queue.shift();
3935
+ startLength--;
3936
+ }
3937
+
3938
+ if ( fn ) {
3939
+
3940
+ // Add a progress sentinel to prevent the fx queue from being
3941
+ // automatically dequeued
3942
+ if ( type === "fx" ) {
3943
+ queue.unshift( "inprogress" );
3944
+ }
3945
+
3946
+ // clear up the last queue stop function
3947
+ delete hooks.stop;
3948
+ fn.call( elem, next, hooks );
3949
+ }
3950
+
3951
+ if ( !startLength && hooks ) {
3952
+ hooks.empty.fire();
3953
+ }
3954
+ },
3955
+
3956
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
3957
+ _queueHooks: function( elem, type ) {
3958
+ var key = type + "queueHooks";
3959
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
3960
+ empty: jQuery.Callbacks("once memory").add(function() {
3961
+ jQuery._removeData( elem, type + "queue" );
3962
+ jQuery._removeData( elem, key );
3963
+ })
3964
+ });
3965
+ }
3966
+ });
3967
+
3968
+ jQuery.fn.extend({
3969
+ queue: function( type, data ) {
3970
+ var setter = 2;
3971
+
3972
+ if ( typeof type !== "string" ) {
3973
+ data = type;
3974
+ type = "fx";
3975
+ setter--;
3976
+ }
3977
+
3978
+ if ( arguments.length < setter ) {
3979
+ return jQuery.queue( this[0], type );
3980
+ }
3981
+
3982
+ return data === undefined ?
3983
+ this :
3984
+ this.each(function() {
3985
+ var queue = jQuery.queue( this, type, data );
3986
+
3987
+ // ensure a hooks for this queue
3988
+ jQuery._queueHooks( this, type );
3989
+
3990
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
3991
+ jQuery.dequeue( this, type );
3992
+ }
3993
+ });
3994
+ },
3995
+ dequeue: function( type ) {
3996
+ return this.each(function() {
3997
+ jQuery.dequeue( this, type );
3998
+ });
3999
+ },
4000
+ // Based off of the plugin by Clint Helfers, with permission.
4001
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
4002
+ delay: function( time, type ) {
4003
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
4004
+ type = type || "fx";
4005
+
4006
+ return this.queue( type, function( next, hooks ) {
4007
+ var timeout = setTimeout( next, time );
4008
+ hooks.stop = function() {
4009
+ clearTimeout( timeout );
4010
+ };
4011
+ });
4012
+ },
4013
+ clearQueue: function( type ) {
4014
+ return this.queue( type || "fx", [] );
4015
+ },
4016
+ // Get a promise resolved when queues of a certain type
4017
+ // are emptied (fx is the type by default)
4018
+ promise: function( type, obj ) {
4019
+ var tmp,
4020
+ count = 1,
4021
+ defer = jQuery.Deferred(),
4022
+ elements = this,
4023
+ i = this.length,
4024
+ resolve = function() {
4025
+ if ( !( --count ) ) {
4026
+ defer.resolveWith( elements, [ elements ] );
4027
+ }
4028
+ };
4029
+
4030
+ if ( typeof type !== "string" ) {
4031
+ obj = type;
4032
+ type = undefined;
4033
+ }
4034
+ type = type || "fx";
4035
+
4036
+ while( i-- ) {
4037
+ tmp = jQuery._data( elements[ i ], type + "queueHooks" );
4038
+ if ( tmp && tmp.empty ) {
4039
+ count++;
4040
+ tmp.empty.add( resolve );
4041
+ }
4042
+ }
4043
+ resolve();
4044
+ return defer.promise( obj );
4045
+ }
4046
+ });
4047
+ var nodeHook, boolHook,
4048
+ rclass = /[\t\r\n\f]/g,
4049
+ rreturn = /\r/g,
4050
+ rfocusable = /^(?:input|select|textarea|button|object)$/i,
4051
+ rclickable = /^(?:a|area)$/i,
4052
+ ruseDefault = /^(?:checked|selected)$/i,
4053
+ getSetAttribute = jQuery.support.getSetAttribute,
4054
+ getSetInput = jQuery.support.input;
4055
+
4056
+ jQuery.fn.extend({
4057
+ attr: function( name, value ) {
4058
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
4059
+ },
4060
+
4061
+ removeAttr: function( name ) {
4062
+ return this.each(function() {
4063
+ jQuery.removeAttr( this, name );
4064
+ });
4065
+ },
4066
+
4067
+ prop: function( name, value ) {
4068
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
4069
+ },
4070
+
4071
+ removeProp: function( name ) {
4072
+ name = jQuery.propFix[ name ] || name;
4073
+ return this.each(function() {
4074
+ // try/catch handles cases where IE balks (such as removing a property on window)
4075
+ try {
4076
+ this[ name ] = undefined;
4077
+ delete this[ name ];
4078
+ } catch( e ) {}
4079
+ });
4080
+ },
4081
+
4082
+ addClass: function( value ) {
4083
+ var classes, elem, cur, clazz, j,
4084
+ i = 0,
4085
+ len = this.length,
4086
+ proceed = typeof value === "string" && value;
4087
+
4088
+ if ( jQuery.isFunction( value ) ) {
4089
+ return this.each(function( j ) {
4090
+ jQuery( this ).addClass( value.call( this, j, this.className ) );
4091
+ });
4092
+ }
4093
+
4094
+ if ( proceed ) {
4095
+ // The disjunction here is for better compressibility (see removeClass)
4096
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
4097
+
4098
+ for ( ; i < len; i++ ) {
4099
+ elem = this[ i ];
4100
+ cur = elem.nodeType === 1 && ( elem.className ?
4101
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
4102
+ " "
4103
+ );
4104
+
4105
+ if ( cur ) {
4106
+ j = 0;
4107
+ while ( (clazz = classes[j++]) ) {
4108
+ if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
4109
+ cur += clazz + " ";
4110
+ }
4111
+ }
4112
+ elem.className = jQuery.trim( cur );
4113
+
4114
+ }
4115
+ }
4116
+ }
4117
+
4118
+ return this;
4119
+ },
4120
+
4121
+ removeClass: function( value ) {
4122
+ var classes, elem, cur, clazz, j,
4123
+ i = 0,
4124
+ len = this.length,
4125
+ proceed = arguments.length === 0 || typeof value === "string" && value;
4126
+
4127
+ if ( jQuery.isFunction( value ) ) {
4128
+ return this.each(function( j ) {
4129
+ jQuery( this ).removeClass( value.call( this, j, this.className ) );
4130
+ });
4131
+ }
4132
+ if ( proceed ) {
4133
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
4134
+
4135
+ for ( ; i < len; i++ ) {
4136
+ elem = this[ i ];
4137
+ // This expression is here for better compressibility (see addClass)
4138
+ cur = elem.nodeType === 1 && ( elem.className ?
4139
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
4140
+ ""
4141
+ );
4142
+
4143
+ if ( cur ) {
4144
+ j = 0;
4145
+ while ( (clazz = classes[j++]) ) {
4146
+ // Remove *all* instances
4147
+ while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
4148
+ cur = cur.replace( " " + clazz + " ", " " );
4149
+ }
4150
+ }
4151
+ elem.className = value ? jQuery.trim( cur ) : "";
4152
+ }
4153
+ }
4154
+ }
4155
+
4156
+ return this;
4157
+ },
4158
+
4159
+ toggleClass: function( value, stateVal ) {
4160
+ var type = typeof value;
4161
+
4162
+ if ( typeof stateVal === "boolean" && type === "string" ) {
4163
+ return stateVal ? this.addClass( value ) : this.removeClass( value );
4164
+ }
4165
+
4166
+ if ( jQuery.isFunction( value ) ) {
4167
+ return this.each(function( i ) {
4168
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
4169
+ });
4170
+ }
4171
+
4172
+ return this.each(function() {
4173
+ if ( type === "string" ) {
4174
+ // toggle individual class names
4175
+ var className,
4176
+ i = 0,
4177
+ self = jQuery( this ),
4178
+ classNames = value.match( core_rnotwhite ) || [];
4179
+
4180
+ while ( (className = classNames[ i++ ]) ) {
4181
+ // check each className given, space separated list
4182
+ if ( self.hasClass( className ) ) {
4183
+ self.removeClass( className );
4184
+ } else {
4185
+ self.addClass( className );
4186
+ }
4187
+ }
4188
+
4189
+ // Toggle whole class name
4190
+ } else if ( type === core_strundefined || type === "boolean" ) {
4191
+ if ( this.className ) {
4192
+ // store className if set
4193
+ jQuery._data( this, "__className__", this.className );
4194
+ }
4195
+
4196
+ // If the element has a class name or if we're passed "false",
4197
+ // then remove the whole classname (if there was one, the above saved it).
4198
+ // Otherwise bring back whatever was previously saved (if anything),
4199
+ // falling back to the empty string if nothing was stored.
4200
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
4201
+ }
4202
+ });
4203
+ },
4204
+
4205
+ hasClass: function( selector ) {
4206
+ var className = " " + selector + " ",
4207
+ i = 0,
4208
+ l = this.length;
4209
+ for ( ; i < l; i++ ) {
4210
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
4211
+ return true;
4212
+ }
4213
+ }
4214
+
4215
+ return false;
4216
+ },
4217
+
4218
+ val: function( value ) {
4219
+ var ret, hooks, isFunction,
4220
+ elem = this[0];
4221
+
4222
+ if ( !arguments.length ) {
4223
+ if ( elem ) {
4224
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
4225
+
4226
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
4227
+ return ret;
4228
+ }
4229
+
4230
+ ret = elem.value;
4231
+
4232
+ return typeof ret === "string" ?
4233
+ // handle most common string cases
4234
+ ret.replace(rreturn, "") :
4235
+ // handle cases where value is null/undef or number
4236
+ ret == null ? "" : ret;
4237
+ }
4238
+
4239
+ return;
4240
+ }
4241
+
4242
+ isFunction = jQuery.isFunction( value );
4243
+
4244
+ return this.each(function( i ) {
4245
+ var val;
4246
+
4247
+ if ( this.nodeType !== 1 ) {
4248
+ return;
4249
+ }
4250
+
4251
+ if ( isFunction ) {
4252
+ val = value.call( this, i, jQuery( this ).val() );
4253
+ } else {
4254
+ val = value;
4255
+ }
4256
+
4257
+ // Treat null/undefined as ""; convert numbers to string
4258
+ if ( val == null ) {
4259
+ val = "";
4260
+ } else if ( typeof val === "number" ) {
4261
+ val += "";
4262
+ } else if ( jQuery.isArray( val ) ) {
4263
+ val = jQuery.map(val, function ( value ) {
4264
+ return value == null ? "" : value + "";
4265
+ });
4266
+ }
4267
+
4268
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
4269
+
4270
+ // If set returns undefined, fall back to normal setting
4271
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
4272
+ this.value = val;
4273
+ }
4274
+ });
4275
+ }
4276
+ });
4277
+
4278
+ jQuery.extend({
4279
+ valHooks: {
4280
+ option: {
4281
+ get: function( elem ) {
4282
+ // Use proper attribute retrieval(#6932, #12072)
4283
+ var val = jQuery.find.attr( elem, "value" );
4284
+ return val != null ?
4285
+ val :
4286
+ elem.text;
4287
+ }
4288
+ },
4289
+ select: {
4290
+ get: function( elem ) {
4291
+ var value, option,
4292
+ options = elem.options,
4293
+ index = elem.selectedIndex,
4294
+ one = elem.type === "select-one" || index < 0,
4295
+ values = one ? null : [],
4296
+ max = one ? index + 1 : options.length,
4297
+ i = index < 0 ?
4298
+ max :
4299
+ one ? index : 0;
4300
+
4301
+ // Loop through all the selected options
4302
+ for ( ; i < max; i++ ) {
4303
+ option = options[ i ];
4304
+
4305
+ // oldIE doesn't update selected after form reset (#2551)
4306
+ if ( ( option.selected || i === index ) &&
4307
+ // Don't return options that are disabled or in a disabled optgroup
4308
+ ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
4309
+ ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
4310
+
4311
+ // Get the specific value for the option
4312
+ value = jQuery( option ).val();
4313
+
4314
+ // We don't need an array for one selects
4315
+ if ( one ) {
4316
+ return value;
4317
+ }
4318
+
4319
+ // Multi-Selects return an array
4320
+ values.push( value );
4321
+ }
4322
+ }
4323
+
4324
+ return values;
4325
+ },
4326
+
4327
+ set: function( elem, value ) {
4328
+ var optionSet, option,
4329
+ options = elem.options,
4330
+ values = jQuery.makeArray( value ),
4331
+ i = options.length;
4332
+
4333
+ while ( i-- ) {
4334
+ option = options[ i ];
4335
+ if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
4336
+ optionSet = true;
4337
+ }
4338
+ }
4339
+
4340
+ // force browsers to behave consistently when non-matching value is set
4341
+ if ( !optionSet ) {
4342
+ elem.selectedIndex = -1;
4343
+ }
4344
+ return values;
4345
+ }
4346
+ }
4347
+ },
4348
+
4349
+ attr: function( elem, name, value ) {
4350
+ var hooks, ret,
4351
+ nType = elem.nodeType;
4352
+
4353
+ // don't get/set attributes on text, comment and attribute nodes
4354
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
4355
+ return;
4356
+ }
4357
+
4358
+ // Fallback to prop when attributes are not supported
4359
+ if ( typeof elem.getAttribute === core_strundefined ) {
4360
+ return jQuery.prop( elem, name, value );
4361
+ }
4362
+
4363
+ // All attributes are lowercase
4364
+ // Grab necessary hook if one is defined
4365
+ if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
4366
+ name = name.toLowerCase();
4367
+ hooks = jQuery.attrHooks[ name ] ||
4368
+ ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
4369
+ }
4370
+
4371
+ if ( value !== undefined ) {
4372
+
4373
+ if ( value === null ) {
4374
+ jQuery.removeAttr( elem, name );
4375
+
4376
+ } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
4377
+ return ret;
4378
+
4379
+ } else {
4380
+ elem.setAttribute( name, value + "" );
4381
+ return value;
4382
+ }
4383
+
4384
+ } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
4385
+ return ret;
4386
+
4387
+ } else {
4388
+ ret = jQuery.find.attr( elem, name );
4389
+
4390
+ // Non-existent attributes return null, we normalize to undefined
4391
+ return ret == null ?
4392
+ undefined :
4393
+ ret;
4394
+ }
4395
+ },
4396
+
4397
+ removeAttr: function( elem, value ) {
4398
+ var name, propName,
4399
+ i = 0,
4400
+ attrNames = value && value.match( core_rnotwhite );
4401
+
4402
+ if ( attrNames && elem.nodeType === 1 ) {
4403
+ while ( (name = attrNames[i++]) ) {
4404
+ propName = jQuery.propFix[ name ] || name;
4405
+
4406
+ // Boolean attributes get special treatment (#10870)
4407
+ if ( jQuery.expr.match.bool.test( name ) ) {
4408
+ // Set corresponding property to false
4409
+ if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
4410
+ elem[ propName ] = false;
4411
+ // Support: IE<9
4412
+ // Also clear defaultChecked/defaultSelected (if appropriate)
4413
+ } else {
4414
+ elem[ jQuery.camelCase( "default-" + name ) ] =
4415
+ elem[ propName ] = false;
4416
+ }
4417
+
4418
+ // See #9699 for explanation of this approach (setting first, then removal)
4419
+ } else {
4420
+ jQuery.attr( elem, name, "" );
4421
+ }
4422
+
4423
+ elem.removeAttribute( getSetAttribute ? name : propName );
4424
+ }
4425
+ }
4426
+ },
4427
+
4428
+ attrHooks: {
4429
+ type: {
4430
+ set: function( elem, value ) {
4431
+ if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
4432
+ // Setting the type on a radio button after the value resets the value in IE6-9
4433
+ // Reset value to default in case type is set after value during creation
4434
+ var val = elem.value;
4435
+ elem.setAttribute( "type", value );
4436
+ if ( val ) {
4437
+ elem.value = val;
4438
+ }
4439
+ return value;
4440
+ }
4441
+ }
4442
+ }
4443
+ },
4444
+
4445
+ propFix: {
4446
+ "for": "htmlFor",
4447
+ "class": "className"
4448
+ },
4449
+
4450
+ prop: function( elem, name, value ) {
4451
+ var ret, hooks, notxml,
4452
+ nType = elem.nodeType;
4453
+
4454
+ // don't get/set properties on text, comment and attribute nodes
4455
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
4456
+ return;
4457
+ }
4458
+
4459
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
4460
+
4461
+ if ( notxml ) {
4462
+ // Fix name and attach hooks
4463
+ name = jQuery.propFix[ name ] || name;
4464
+ hooks = jQuery.propHooks[ name ];
4465
+ }
4466
+
4467
+ if ( value !== undefined ) {
4468
+ return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
4469
+ ret :
4470
+ ( elem[ name ] = value );
4471
+
4472
+ } else {
4473
+ return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
4474
+ ret :
4475
+ elem[ name ];
4476
+ }
4477
+ },
4478
+
4479
+ propHooks: {
4480
+ tabIndex: {
4481
+ get: function( elem ) {
4482
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
4483
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
4484
+ // Use proper attribute retrieval(#12072)
4485
+ var tabindex = jQuery.find.attr( elem, "tabindex" );
4486
+
4487
+ return tabindex ?
4488
+ parseInt( tabindex, 10 ) :
4489
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
4490
+ 0 :
4491
+ -1;
4492
+ }
4493
+ }
4494
+ }
4495
+ });
4496
+
4497
+ // Hooks for boolean attributes
4498
+ boolHook = {
4499
+ set: function( elem, value, name ) {
4500
+ if ( value === false ) {
4501
+ // Remove boolean attributes when set to false
4502
+ jQuery.removeAttr( elem, name );
4503
+ } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
4504
+ // IE<8 needs the *property* name
4505
+ elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
4506
+
4507
+ // Use defaultChecked and defaultSelected for oldIE
4508
+ } else {
4509
+ elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
4510
+ }
4511
+
4512
+ return name;
4513
+ }
4514
+ };
4515
+ jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
4516
+ var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
4517
+
4518
+ jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
4519
+ function( elem, name, isXML ) {
4520
+ var fn = jQuery.expr.attrHandle[ name ],
4521
+ ret = isXML ?
4522
+ undefined :
4523
+ /* jshint eqeqeq: false */
4524
+ (jQuery.expr.attrHandle[ name ] = undefined) !=
4525
+ getter( elem, name, isXML ) ?
4526
+
4527
+ name.toLowerCase() :
4528
+ null;
4529
+ jQuery.expr.attrHandle[ name ] = fn;
4530
+ return ret;
4531
+ } :
4532
+ function( elem, name, isXML ) {
4533
+ return isXML ?
4534
+ undefined :
4535
+ elem[ jQuery.camelCase( "default-" + name ) ] ?
4536
+ name.toLowerCase() :
4537
+ null;
4538
+ };
4539
+ });
4540
+
4541
+ // fix oldIE attroperties
4542
+ if ( !getSetInput || !getSetAttribute ) {
4543
+ jQuery.attrHooks.value = {
4544
+ set: function( elem, value, name ) {
4545
+ if ( jQuery.nodeName( elem, "input" ) ) {
4546
+ // Does not return so that setAttribute is also used
4547
+ elem.defaultValue = value;
4548
+ } else {
4549
+ // Use nodeHook if defined (#1954); otherwise setAttribute is fine
4550
+ return nodeHook && nodeHook.set( elem, value, name );
4551
+ }
4552
+ }
4553
+ };
4554
+ }
4555
+
4556
+ // IE6/7 do not support getting/setting some attributes with get/setAttribute
4557
+ if ( !getSetAttribute ) {
4558
+
4559
+ // Use this for any attribute in IE6/7
4560
+ // This fixes almost every IE6/7 issue
4561
+ nodeHook = {
4562
+ set: function( elem, value, name ) {
4563
+ // Set the existing or create a new attribute node
4564
+ var ret = elem.getAttributeNode( name );
4565
+ if ( !ret ) {
4566
+ elem.setAttributeNode(
4567
+ (ret = elem.ownerDocument.createAttribute( name ))
4568
+ );
4569
+ }
4570
+
4571
+ ret.value = value += "";
4572
+
4573
+ // Break association with cloned elements by also using setAttribute (#9646)
4574
+ return name === "value" || value === elem.getAttribute( name ) ?
4575
+ value :
4576
+ undefined;
4577
+ }
4578
+ };
4579
+ jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords =
4580
+ // Some attributes are constructed with empty-string values when not defined
4581
+ function( elem, name, isXML ) {
4582
+ var ret;
4583
+ return isXML ?
4584
+ undefined :
4585
+ (ret = elem.getAttributeNode( name )) && ret.value !== "" ?
4586
+ ret.value :
4587
+ null;
4588
+ };
4589
+ jQuery.valHooks.button = {
4590
+ get: function( elem, name ) {
4591
+ var ret = elem.getAttributeNode( name );
4592
+ return ret && ret.specified ?
4593
+ ret.value :
4594
+ undefined;
4595
+ },
4596
+ set: nodeHook.set
4597
+ };
4598
+
4599
+ // Set contenteditable to false on removals(#10429)
4600
+ // Setting to empty string throws an error as an invalid value
4601
+ jQuery.attrHooks.contenteditable = {
4602
+ set: function( elem, value, name ) {
4603
+ nodeHook.set( elem, value === "" ? false : value, name );
4604
+ }
4605
+ };
4606
+
4607
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
4608
+ // This is for removals
4609
+ jQuery.each([ "width", "height" ], function( i, name ) {
4610
+ jQuery.attrHooks[ name ] = {
4611
+ set: function( elem, value ) {
4612
+ if ( value === "" ) {
4613
+ elem.setAttribute( name, "auto" );
4614
+ return value;
4615
+ }
4616
+ }
4617
+ };
4618
+ });
4619
+ }
4620
+
4621
+
4622
+ // Some attributes require a special call on IE
4623
+ // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
4624
+ if ( !jQuery.support.hrefNormalized ) {
4625
+ // href/src property should get the full normalized URL (#10299/#12915)
4626
+ jQuery.each([ "href", "src" ], function( i, name ) {
4627
+ jQuery.propHooks[ name ] = {
4628
+ get: function( elem ) {
4629
+ return elem.getAttribute( name, 4 );
4630
+ }
4631
+ };
4632
+ });
4633
+ }
4634
+
4635
+ if ( !jQuery.support.style ) {
4636
+ jQuery.attrHooks.style = {
4637
+ get: function( elem ) {
4638
+ // Return undefined in the case of empty string
4639
+ // Note: IE uppercases css property names, but if we were to .toLowerCase()
4640
+ // .cssText, that would destroy case senstitivity in URL's, like in "background"
4641
+ return elem.style.cssText || undefined;
4642
+ },
4643
+ set: function( elem, value ) {
4644
+ return ( elem.style.cssText = value + "" );
4645
+ }
4646
+ };
4647
+ }
4648
+
4649
+ // Safari mis-reports the default selected property of an option
4650
+ // Accessing the parent's selectedIndex property fixes it
4651
+ if ( !jQuery.support.optSelected ) {
4652
+ jQuery.propHooks.selected = {
4653
+ get: function( elem ) {
4654
+ var parent = elem.parentNode;
4655
+
4656
+ if ( parent ) {
4657
+ parent.selectedIndex;
4658
+
4659
+ // Make sure that it also works with optgroups, see #5701
4660
+ if ( parent.parentNode ) {
4661
+ parent.parentNode.selectedIndex;
4662
+ }
4663
+ }
4664
+ return null;
4665
+ }
4666
+ };
4667
+ }
4668
+
4669
+ jQuery.each([
4670
+ "tabIndex",
4671
+ "readOnly",
4672
+ "maxLength",
4673
+ "cellSpacing",
4674
+ "cellPadding",
4675
+ "rowSpan",
4676
+ "colSpan",
4677
+ "useMap",
4678
+ "frameBorder",
4679
+ "contentEditable"
4680
+ ], function() {
4681
+ jQuery.propFix[ this.toLowerCase() ] = this;
4682
+ });
4683
+
4684
+ // IE6/7 call enctype encoding
4685
+ if ( !jQuery.support.enctype ) {
4686
+ jQuery.propFix.enctype = "encoding";
4687
+ }
4688
+
4689
+ // Radios and checkboxes getter/setter
4690
+ jQuery.each([ "radio", "checkbox" ], function() {
4691
+ jQuery.valHooks[ this ] = {
4692
+ set: function( elem, value ) {
4693
+ if ( jQuery.isArray( value ) ) {
4694
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
4695
+ }
4696
+ }
4697
+ };
4698
+ if ( !jQuery.support.checkOn ) {
4699
+ jQuery.valHooks[ this ].get = function( elem ) {
4700
+ // Support: Webkit
4701
+ // "" is returned instead of "on" if a value isn't specified
4702
+ return elem.getAttribute("value") === null ? "on" : elem.value;
4703
+ };
4704
+ }
4705
+ });
4706
+ var rformElems = /^(?:input|select|textarea)$/i,
4707
+ rkeyEvent = /^key/,
4708
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
4709
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
4710
+ rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
4711
+
4712
+ function returnTrue() {
4713
+ return true;
4714
+ }
4715
+
4716
+ function returnFalse() {
4717
+ return false;
4718
+ }
4719
+
4720
+ function safeActiveElement() {
4721
+ try {
4722
+ return document.activeElement;
4723
+ } catch ( err ) { }
4724
+ }
4725
+
4726
+ /*
4727
+ * Helper functions for managing events -- not part of the public interface.
4728
+ * Props to Dean Edwards' addEvent library for many of the ideas.
4729
+ */
4730
+ jQuery.event = {
4731
+
4732
+ global: {},
4733
+
4734
+ add: function( elem, types, handler, data, selector ) {
4735
+ var tmp, events, t, handleObjIn,
4736
+ special, eventHandle, handleObj,
4737
+ handlers, type, namespaces, origType,
4738
+ elemData = jQuery._data( elem );
4739
+
4740
+ // Don't attach events to noData or text/comment nodes (but allow plain objects)
4741
+ if ( !elemData ) {
4742
+ return;
4743
+ }
4744
+
4745
+ // Caller can pass in an object of custom data in lieu of the handler
4746
+ if ( handler.handler ) {
4747
+ handleObjIn = handler;
4748
+ handler = handleObjIn.handler;
4749
+ selector = handleObjIn.selector;
4750
+ }
4751
+
4752
+ // Make sure that the handler has a unique ID, used to find/remove it later
4753
+ if ( !handler.guid ) {
4754
+ handler.guid = jQuery.guid++;
4755
+ }
4756
+
4757
+ // Init the element's event structure and main handler, if this is the first
4758
+ if ( !(events = elemData.events) ) {
4759
+ events = elemData.events = {};
4760
+ }
4761
+ if ( !(eventHandle = elemData.handle) ) {
4762
+ eventHandle = elemData.handle = function( e ) {
4763
+ // Discard the second event of a jQuery.event.trigger() and
4764
+ // when an event is called after a page has unloaded
4765
+ return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
4766
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
4767
+ undefined;
4768
+ };
4769
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
4770
+ eventHandle.elem = elem;
4771
+ }
4772
+
4773
+ // Handle multiple events separated by a space
4774
+ types = ( types || "" ).match( core_rnotwhite ) || [""];
4775
+ t = types.length;
4776
+ while ( t-- ) {
4777
+ tmp = rtypenamespace.exec( types[t] ) || [];
4778
+ type = origType = tmp[1];
4779
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
4780
+
4781
+ // There *must* be a type, no attaching namespace-only handlers
4782
+ if ( !type ) {
4783
+ continue;
4784
+ }
4785
+
4786
+ // If event changes its type, use the special event handlers for the changed type
4787
+ special = jQuery.event.special[ type ] || {};
4788
+
4789
+ // If selector defined, determine special event api type, otherwise given type
4790
+ type = ( selector ? special.delegateType : special.bindType ) || type;
4791
+
4792
+ // Update special based on newly reset type
4793
+ special = jQuery.event.special[ type ] || {};
4794
+
4795
+ // handleObj is passed to all event handlers
4796
+ handleObj = jQuery.extend({
4797
+ type: type,
4798
+ origType: origType,
4799
+ data: data,
4800
+ handler: handler,
4801
+ guid: handler.guid,
4802
+ selector: selector,
4803
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
4804
+ namespace: namespaces.join(".")
4805
+ }, handleObjIn );
4806
+
4807
+ // Init the event handler queue if we're the first
4808
+ if ( !(handlers = events[ type ]) ) {
4809
+ handlers = events[ type ] = [];
4810
+ handlers.delegateCount = 0;
4811
+
4812
+ // Only use addEventListener/attachEvent if the special events handler returns false
4813
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
4814
+ // Bind the global event handler to the element
4815
+ if ( elem.addEventListener ) {
4816
+ elem.addEventListener( type, eventHandle, false );
4817
+
4818
+ } else if ( elem.attachEvent ) {
4819
+ elem.attachEvent( "on" + type, eventHandle );
4820
+ }
4821
+ }
4822
+ }
4823
+
4824
+ if ( special.add ) {
4825
+ special.add.call( elem, handleObj );
4826
+
4827
+ if ( !handleObj.handler.guid ) {
4828
+ handleObj.handler.guid = handler.guid;
4829
+ }
4830
+ }
4831
+
4832
+ // Add to the element's handler list, delegates in front
4833
+ if ( selector ) {
4834
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
4835
+ } else {
4836
+ handlers.push( handleObj );
4837
+ }
4838
+
4839
+ // Keep track of which events have ever been used, for event optimization
4840
+ jQuery.event.global[ type ] = true;
4841
+ }
4842
+
4843
+ // Nullify elem to prevent memory leaks in IE
4844
+ elem = null;
4845
+ },
4846
+
4847
+ // Detach an event or set of events from an element
4848
+ remove: function( elem, types, handler, selector, mappedTypes ) {
4849
+ var j, handleObj, tmp,
4850
+ origCount, t, events,
4851
+ special, handlers, type,
4852
+ namespaces, origType,
4853
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
4854
+
4855
+ if ( !elemData || !(events = elemData.events) ) {
4856
+ return;
4857
+ }
4858
+
4859
+ // Once for each type.namespace in types; type may be omitted
4860
+ types = ( types || "" ).match( core_rnotwhite ) || [""];
4861
+ t = types.length;
4862
+ while ( t-- ) {
4863
+ tmp = rtypenamespace.exec( types[t] ) || [];
4864
+ type = origType = tmp[1];
4865
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
4866
+
4867
+ // Unbind all events (on this namespace, if provided) for the element
4868
+ if ( !type ) {
4869
+ for ( type in events ) {
4870
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
4871
+ }
4872
+ continue;
4873
+ }
4874
+
4875
+ special = jQuery.event.special[ type ] || {};
4876
+ type = ( selector ? special.delegateType : special.bindType ) || type;
4877
+ handlers = events[ type ] || [];
4878
+ tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
4879
+
4880
+ // Remove matching events
4881
+ origCount = j = handlers.length;
4882
+ while ( j-- ) {
4883
+ handleObj = handlers[ j ];
4884
+
4885
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
4886
+ ( !handler || handler.guid === handleObj.guid ) &&
4887
+ ( !tmp || tmp.test( handleObj.namespace ) ) &&
4888
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
4889
+ handlers.splice( j, 1 );
4890
+
4891
+ if ( handleObj.selector ) {
4892
+ handlers.delegateCount--;
4893
+ }
4894
+ if ( special.remove ) {
4895
+ special.remove.call( elem, handleObj );
4896
+ }
4897
+ }
4898
+ }
4899
+
4900
+ // Remove generic event handler if we removed something and no more handlers exist
4901
+ // (avoids potential for endless recursion during removal of special event handlers)
4902
+ if ( origCount && !handlers.length ) {
4903
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
4904
+ jQuery.removeEvent( elem, type, elemData.handle );
4905
+ }
4906
+
4907
+ delete events[ type ];
4908
+ }
4909
+ }
4910
+
4911
+ // Remove the expando if it's no longer used
4912
+ if ( jQuery.isEmptyObject( events ) ) {
4913
+ delete elemData.handle;
4914
+
4915
+ // removeData also checks for emptiness and clears the expando if empty
4916
+ // so use it instead of delete
4917
+ jQuery._removeData( elem, "events" );
4918
+ }
4919
+ },
4920
+
4921
+ trigger: function( event, data, elem, onlyHandlers ) {
4922
+ var handle, ontype, cur,
4923
+ bubbleType, special, tmp, i,
4924
+ eventPath = [ elem || document ],
4925
+ type = core_hasOwn.call( event, "type" ) ? event.type : event,
4926
+ namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
4927
+
4928
+ cur = tmp = elem = elem || document;
4929
+
4930
+ // Don't do events on text and comment nodes
4931
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
4932
+ return;
4933
+ }
4934
+
4935
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
4936
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
4937
+ return;
4938
+ }
4939
+
4940
+ if ( type.indexOf(".") >= 0 ) {
4941
+ // Namespaced trigger; create a regexp to match event type in handle()
4942
+ namespaces = type.split(".");
4943
+ type = namespaces.shift();
4944
+ namespaces.sort();
4945
+ }
4946
+ ontype = type.indexOf(":") < 0 && "on" + type;
4947
+
4948
+ // Caller can pass in a jQuery.Event object, Object, or just an event type string
4949
+ event = event[ jQuery.expando ] ?
4950
+ event :
4951
+ new jQuery.Event( type, typeof event === "object" && event );
4952
+
4953
+ // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
4954
+ event.isTrigger = onlyHandlers ? 2 : 3;
4955
+ event.namespace = namespaces.join(".");
4956
+ event.namespace_re = event.namespace ?
4957
+ new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
4958
+ null;
4959
+
4960
+ // Clean up the event in case it is being reused
4961
+ event.result = undefined;
4962
+ if ( !event.target ) {
4963
+ event.target = elem;
4964
+ }
4965
+
4966
+ // Clone any incoming data and prepend the event, creating the handler arg list
4967
+ data = data == null ?
4968
+ [ event ] :
4969
+ jQuery.makeArray( data, [ event ] );
4970
+
4971
+ // Allow special events to draw outside the lines
4972
+ special = jQuery.event.special[ type ] || {};
4973
+ if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
4974
+ return;
4975
+ }
4976
+
4977
+ // Determine event propagation path in advance, per W3C events spec (#9951)
4978
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
4979
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
4980
+
4981
+ bubbleType = special.delegateType || type;
4982
+ if ( !rfocusMorph.test( bubbleType + type ) ) {
4983
+ cur = cur.parentNode;
4984
+ }
4985
+ for ( ; cur; cur = cur.parentNode ) {
4986
+ eventPath.push( cur );
4987
+ tmp = cur;
4988
+ }
4989
+
4990
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
4991
+ if ( tmp === (elem.ownerDocument || document) ) {
4992
+ eventPath.push( tmp.defaultView || tmp.parentWindow || window );
4993
+ }
4994
+ }
4995
+
4996
+ // Fire handlers on the event path
4997
+ i = 0;
4998
+ while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
4999
+
5000
+ event.type = i > 1 ?
5001
+ bubbleType :
5002
+ special.bindType || type;
5003
+
5004
+ // jQuery handler
5005
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
5006
+ if ( handle ) {
5007
+ handle.apply( cur, data );
5008
+ }
5009
+
5010
+ // Native handler
5011
+ handle = ontype && cur[ ontype ];
5012
+ if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
5013
+ event.preventDefault();
5014
+ }
5015
+ }
5016
+ event.type = type;
5017
+
5018
+ // If nobody prevented the default action, do it now
5019
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
5020
+
5021
+ if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
5022
+ jQuery.acceptData( elem ) ) {
5023
+
5024
+ // Call a native DOM method on the target with the same name name as the event.
5025
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
5026
+ // Don't do default actions on window, that's where global variables be (#6170)
5027
+ if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
5028
+
5029
+ // Don't re-trigger an onFOO event when we call its FOO() method
5030
+ tmp = elem[ ontype ];
5031
+
5032
+ if ( tmp ) {
5033
+ elem[ ontype ] = null;
5034
+ }
5035
+
5036
+ // Prevent re-triggering of the same event, since we already bubbled it above
5037
+ jQuery.event.triggered = type;
5038
+ try {
5039
+ elem[ type ]();
5040
+ } catch ( e ) {
5041
+ // IE<9 dies on focus/blur to hidden element (#1486,#12518)
5042
+ // only reproducible on winXP IE8 native, not IE9 in IE8 mode
5043
+ }
5044
+ jQuery.event.triggered = undefined;
5045
+
5046
+ if ( tmp ) {
5047
+ elem[ ontype ] = tmp;
5048
+ }
5049
+ }
5050
+ }
5051
+ }
5052
+
5053
+ return event.result;
5054
+ },
5055
+
5056
+ dispatch: function( event ) {
5057
+
5058
+ // Make a writable jQuery.Event from the native event object
5059
+ event = jQuery.event.fix( event );
5060
+
5061
+ var i, ret, handleObj, matched, j,
5062
+ handlerQueue = [],
5063
+ args = core_slice.call( arguments ),
5064
+ handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
5065
+ special = jQuery.event.special[ event.type ] || {};
5066
+
5067
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
5068
+ args[0] = event;
5069
+ event.delegateTarget = this;
5070
+
5071
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
5072
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
5073
+ return;
5074
+ }
5075
+
5076
+ // Determine handlers
5077
+ handlerQueue = jQuery.event.handlers.call( this, event, handlers );
5078
+
5079
+ // Run delegates first; they may want to stop propagation beneath us
5080
+ i = 0;
5081
+ while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
5082
+ event.currentTarget = matched.elem;
5083
+
5084
+ j = 0;
5085
+ while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
5086
+
5087
+ // Triggered event must either 1) have no namespace, or
5088
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
5089
+ if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
5090
+
5091
+ event.handleObj = handleObj;
5092
+ event.data = handleObj.data;
5093
+
5094
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
5095
+ .apply( matched.elem, args );
5096
+
5097
+ if ( ret !== undefined ) {
5098
+ if ( (event.result = ret) === false ) {
5099
+ event.preventDefault();
5100
+ event.stopPropagation();
5101
+ }
5102
+ }
5103
+ }
5104
+ }
5105
+ }
5106
+
5107
+ // Call the postDispatch hook for the mapped type
5108
+ if ( special.postDispatch ) {
5109
+ special.postDispatch.call( this, event );
5110
+ }
5111
+
5112
+ return event.result;
5113
+ },
5114
+
5115
+ handlers: function( event, handlers ) {
5116
+ var sel, handleObj, matches, i,
5117
+ handlerQueue = [],
5118
+ delegateCount = handlers.delegateCount,
5119
+ cur = event.target;
5120
+
5121
+ // Find delegate handlers
5122
+ // Black-hole SVG <use> instance trees (#13180)
5123
+ // Avoid non-left-click bubbling in Firefox (#3861)
5124
+ if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
5125
+
5126
+ /* jshint eqeqeq: false */
5127
+ for ( ; cur != this; cur = cur.parentNode || this ) {
5128
+ /* jshint eqeqeq: true */
5129
+
5130
+ // Don't check non-elements (#13208)
5131
+ // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
5132
+ if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
5133
+ matches = [];
5134
+ for ( i = 0; i < delegateCount; i++ ) {
5135
+ handleObj = handlers[ i ];
5136
+
5137
+ // Don't conflict with Object.prototype properties (#13203)
5138
+ sel = handleObj.selector + " ";
5139
+
5140
+ if ( matches[ sel ] === undefined ) {
5141
+ matches[ sel ] = handleObj.needsContext ?
5142
+ jQuery( sel, this ).index( cur ) >= 0 :
5143
+ jQuery.find( sel, this, null, [ cur ] ).length;
5144
+ }
5145
+ if ( matches[ sel ] ) {
5146
+ matches.push( handleObj );
5147
+ }
5148
+ }
5149
+ if ( matches.length ) {
5150
+ handlerQueue.push({ elem: cur, handlers: matches });
5151
+ }
5152
+ }
5153
+ }
5154
+ }
5155
+
5156
+ // Add the remaining (directly-bound) handlers
5157
+ if ( delegateCount < handlers.length ) {
5158
+ handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
5159
+ }
5160
+
5161
+ return handlerQueue;
5162
+ },
5163
+
5164
+ fix: function( event ) {
5165
+ if ( event[ jQuery.expando ] ) {
5166
+ return event;
5167
+ }
5168
+
5169
+ // Create a writable copy of the event object and normalize some properties
5170
+ var i, prop, copy,
5171
+ type = event.type,
5172
+ originalEvent = event,
5173
+ fixHook = this.fixHooks[ type ];
5174
+
5175
+ if ( !fixHook ) {
5176
+ this.fixHooks[ type ] = fixHook =
5177
+ rmouseEvent.test( type ) ? this.mouseHooks :
5178
+ rkeyEvent.test( type ) ? this.keyHooks :
5179
+ {};
5180
+ }
5181
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
5182
+
5183
+ event = new jQuery.Event( originalEvent );
5184
+
5185
+ i = copy.length;
5186
+ while ( i-- ) {
5187
+ prop = copy[ i ];
5188
+ event[ prop ] = originalEvent[ prop ];
5189
+ }
5190
+
5191
+ // Support: IE<9
5192
+ // Fix target property (#1925)
5193
+ if ( !event.target ) {
5194
+ event.target = originalEvent.srcElement || document;
5195
+ }
5196
+
5197
+ // Support: Chrome 23+, Safari?
5198
+ // Target should not be a text node (#504, #13143)
5199
+ if ( event.target.nodeType === 3 ) {
5200
+ event.target = event.target.parentNode;
5201
+ }
5202
+
5203
+ // Support: IE<9
5204
+ // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
5205
+ event.metaKey = !!event.metaKey;
5206
+
5207
+ return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
5208
+ },
5209
+
5210
+ // Includes some event props shared by KeyEvent and MouseEvent
5211
+ props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
5212
+
5213
+ fixHooks: {},
5214
+
5215
+ keyHooks: {
5216
+ props: "char charCode key keyCode".split(" "),
5217
+ filter: function( event, original ) {
5218
+
5219
+ // Add which for key events
5220
+ if ( event.which == null ) {
5221
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
5222
+ }
5223
+
5224
+ return event;
5225
+ }
5226
+ },
5227
+
5228
+ mouseHooks: {
5229
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
5230
+ filter: function( event, original ) {
5231
+ var body, eventDoc, doc,
5232
+ button = original.button,
5233
+ fromElement = original.fromElement;
5234
+
5235
+ // Calculate pageX/Y if missing and clientX/Y available
5236
+ if ( event.pageX == null && original.clientX != null ) {
5237
+ eventDoc = event.target.ownerDocument || document;
5238
+ doc = eventDoc.documentElement;
5239
+ body = eventDoc.body;
5240
+
5241
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
5242
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
5243
+ }
5244
+
5245
+ // Add relatedTarget, if necessary
5246
+ if ( !event.relatedTarget && fromElement ) {
5247
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
5248
+ }
5249
+
5250
+ // Add which for click: 1 === left; 2 === middle; 3 === right
5251
+ // Note: button is not normalized, so don't use it
5252
+ if ( !event.which && button !== undefined ) {
5253
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
5254
+ }
5255
+
5256
+ return event;
5257
+ }
5258
+ },
5259
+
5260
+ special: {
5261
+ load: {
5262
+ // Prevent triggered image.load events from bubbling to window.load
5263
+ noBubble: true
5264
+ },
5265
+ focus: {
5266
+ // Fire native event if possible so blur/focus sequence is correct
5267
+ trigger: function() {
5268
+ if ( this !== safeActiveElement() && this.focus ) {
5269
+ try {
5270
+ this.focus();
5271
+ return false;
5272
+ } catch ( e ) {
5273
+ // Support: IE<9
5274
+ // If we error on focus to hidden element (#1486, #12518),
5275
+ // let .trigger() run the handlers
5276
+ }
5277
+ }
5278
+ },
5279
+ delegateType: "focusin"
5280
+ },
5281
+ blur: {
5282
+ trigger: function() {
5283
+ if ( this === safeActiveElement() && this.blur ) {
5284
+ this.blur();
5285
+ return false;
5286
+ }
5287
+ },
5288
+ delegateType: "focusout"
5289
+ },
5290
+ click: {
5291
+ // For checkbox, fire native event so checked state will be right
5292
+ trigger: function() {
5293
+ if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
5294
+ this.click();
5295
+ return false;
5296
+ }
5297
+ },
5298
+
5299
+ // For cross-browser consistency, don't fire native .click() on links
5300
+ _default: function( event ) {
5301
+ return jQuery.nodeName( event.target, "a" );
5302
+ }
5303
+ },
5304
+
5305
+ beforeunload: {
5306
+ postDispatch: function( event ) {
5307
+
5308
+ // Even when returnValue equals to undefined Firefox will still show alert
5309
+ if ( event.result !== undefined ) {
5310
+ event.originalEvent.returnValue = event.result;
5311
+ }
5312
+ }
5313
+ }
5314
+ },
5315
+
5316
+ simulate: function( type, elem, event, bubble ) {
5317
+ // Piggyback on a donor event to simulate a different one.
5318
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
5319
+ // simulated event prevents default then we do the same on the donor.
5320
+ var e = jQuery.extend(
5321
+ new jQuery.Event(),
5322
+ event,
5323
+ {
5324
+ type: type,
5325
+ isSimulated: true,
5326
+ originalEvent: {}
5327
+ }
5328
+ );
5329
+ if ( bubble ) {
5330
+ jQuery.event.trigger( e, null, elem );
5331
+ } else {
5332
+ jQuery.event.dispatch.call( elem, e );
5333
+ }
5334
+ if ( e.isDefaultPrevented() ) {
5335
+ event.preventDefault();
5336
+ }
5337
+ }
5338
+ };
5339
+
5340
+ jQuery.removeEvent = document.removeEventListener ?
5341
+ function( elem, type, handle ) {
5342
+ if ( elem.removeEventListener ) {
5343
+ elem.removeEventListener( type, handle, false );
5344
+ }
5345
+ } :
5346
+ function( elem, type, handle ) {
5347
+ var name = "on" + type;
5348
+
5349
+ if ( elem.detachEvent ) {
5350
+
5351
+ // #8545, #7054, preventing memory leaks for custom events in IE6-8
5352
+ // detachEvent needed property on element, by name of that event, to properly expose it to GC
5353
+ if ( typeof elem[ name ] === core_strundefined ) {
5354
+ elem[ name ] = null;
5355
+ }
5356
+
5357
+ elem.detachEvent( name, handle );
5358
+ }
5359
+ };
5360
+
5361
+ jQuery.Event = function( src, props ) {
5362
+ // Allow instantiation without the 'new' keyword
5363
+ if ( !(this instanceof jQuery.Event) ) {
5364
+ return new jQuery.Event( src, props );
5365
+ }
5366
+
5367
+ // Event object
5368
+ if ( src && src.type ) {
5369
+ this.originalEvent = src;
5370
+ this.type = src.type;
5371
+
5372
+ // Events bubbling up the document may have been marked as prevented
5373
+ // by a handler lower down the tree; reflect the correct value.
5374
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
5375
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
5376
+
5377
+ // Event type
5378
+ } else {
5379
+ this.type = src;
5380
+ }
5381
+
5382
+ // Put explicitly provided properties onto the event object
5383
+ if ( props ) {
5384
+ jQuery.extend( this, props );
5385
+ }
5386
+
5387
+ // Create a timestamp if incoming event doesn't have one
5388
+ this.timeStamp = src && src.timeStamp || jQuery.now();
5389
+
5390
+ // Mark it as fixed
5391
+ this[ jQuery.expando ] = true;
5392
+ };
5393
+
5394
+ // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
5395
+ // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
5396
+ jQuery.Event.prototype = {
5397
+ isDefaultPrevented: returnFalse,
5398
+ isPropagationStopped: returnFalse,
5399
+ isImmediatePropagationStopped: returnFalse,
5400
+
5401
+ preventDefault: function() {
5402
+ var e = this.originalEvent;
5403
+
5404
+ this.isDefaultPrevented = returnTrue;
5405
+ if ( !e ) {
5406
+ return;
5407
+ }
5408
+
5409
+ // If preventDefault exists, run it on the original event
5410
+ if ( e.preventDefault ) {
5411
+ e.preventDefault();
5412
+
5413
+ // Support: IE
5414
+ // Otherwise set the returnValue property of the original event to false
5415
+ } else {
5416
+ e.returnValue = false;
5417
+ }
5418
+ },
5419
+ stopPropagation: function() {
5420
+ var e = this.originalEvent;
5421
+
5422
+ this.isPropagationStopped = returnTrue;
5423
+ if ( !e ) {
5424
+ return;
5425
+ }
5426
+ // If stopPropagation exists, run it on the original event
5427
+ if ( e.stopPropagation ) {
5428
+ e.stopPropagation();
5429
+ }
5430
+
5431
+ // Support: IE
5432
+ // Set the cancelBubble property of the original event to true
5433
+ e.cancelBubble = true;
5434
+ },
5435
+ stopImmediatePropagation: function() {
5436
+ this.isImmediatePropagationStopped = returnTrue;
5437
+ this.stopPropagation();
5438
+ }
5439
+ };
5440
+
5441
+ // Create mouseenter/leave events using mouseover/out and event-time checks
5442
+ jQuery.each({
5443
+ mouseenter: "mouseover",
5444
+ mouseleave: "mouseout"
5445
+ }, function( orig, fix ) {
5446
+ jQuery.event.special[ orig ] = {
5447
+ delegateType: fix,
5448
+ bindType: fix,
5449
+
5450
+ handle: function( event ) {
5451
+ var ret,
5452
+ target = this,
5453
+ related = event.relatedTarget,
5454
+ handleObj = event.handleObj;
5455
+
5456
+ // For mousenter/leave call the handler if related is outside the target.
5457
+ // NB: No relatedTarget if the mouse left/entered the browser window
5458
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
5459
+ event.type = handleObj.origType;
5460
+ ret = handleObj.handler.apply( this, arguments );
5461
+ event.type = fix;
5462
+ }
5463
+ return ret;
5464
+ }
5465
+ };
5466
+ });
5467
+
5468
+ // IE submit delegation
5469
+ if ( !jQuery.support.submitBubbles ) {
5470
+
5471
+ jQuery.event.special.submit = {
5472
+ setup: function() {
5473
+ // Only need this for delegated form submit events
5474
+ if ( jQuery.nodeName( this, "form" ) ) {
5475
+ return false;
5476
+ }
5477
+
5478
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
5479
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
5480
+ // Node name check avoids a VML-related crash in IE (#9807)
5481
+ var elem = e.target,
5482
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
5483
+ if ( form && !jQuery._data( form, "submitBubbles" ) ) {
5484
+ jQuery.event.add( form, "submit._submit", function( event ) {
5485
+ event._submit_bubble = true;
5486
+ });
5487
+ jQuery._data( form, "submitBubbles", true );
5488
+ }
5489
+ });
5490
+ // return undefined since we don't need an event listener
5491
+ },
5492
+
5493
+ postDispatch: function( event ) {
5494
+ // If form was submitted by the user, bubble the event up the tree
5495
+ if ( event._submit_bubble ) {
5496
+ delete event._submit_bubble;
5497
+ if ( this.parentNode && !event.isTrigger ) {
5498
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
5499
+ }
5500
+ }
5501
+ },
5502
+
5503
+ teardown: function() {
5504
+ // Only need this for delegated form submit events
5505
+ if ( jQuery.nodeName( this, "form" ) ) {
5506
+ return false;
5507
+ }
5508
+
5509
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
5510
+ jQuery.event.remove( this, "._submit" );
5511
+ }
5512
+ };
5513
+ }
5514
+
5515
+ // IE change delegation and checkbox/radio fix
5516
+ if ( !jQuery.support.changeBubbles ) {
5517
+
5518
+ jQuery.event.special.change = {
5519
+
5520
+ setup: function() {
5521
+
5522
+ if ( rformElems.test( this.nodeName ) ) {
5523
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
5524
+ // after a propertychange. Eat the blur-change in special.change.handle.
5525
+ // This still fires onchange a second time for check/radio after blur.
5526
+ if ( this.type === "checkbox" || this.type === "radio" ) {
5527
+ jQuery.event.add( this, "propertychange._change", function( event ) {
5528
+ if ( event.originalEvent.propertyName === "checked" ) {
5529
+ this._just_changed = true;
5530
+ }
5531
+ });
5532
+ jQuery.event.add( this, "click._change", function( event ) {
5533
+ if ( this._just_changed && !event.isTrigger ) {
5534
+ this._just_changed = false;
5535
+ }
5536
+ // Allow triggered, simulated change events (#11500)
5537
+ jQuery.event.simulate( "change", this, event, true );
5538
+ });
5539
+ }
5540
+ return false;
5541
+ }
5542
+ // Delegated event; lazy-add a change handler on descendant inputs
5543
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
5544
+ var elem = e.target;
5545
+
5546
+ if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
5547
+ jQuery.event.add( elem, "change._change", function( event ) {
5548
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
5549
+ jQuery.event.simulate( "change", this.parentNode, event, true );
5550
+ }
5551
+ });
5552
+ jQuery._data( elem, "changeBubbles", true );
5553
+ }
5554
+ });
5555
+ },
5556
+
5557
+ handle: function( event ) {
5558
+ var elem = event.target;
5559
+
5560
+ // Swallow native change events from checkbox/radio, we already triggered them above
5561
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
5562
+ return event.handleObj.handler.apply( this, arguments );
5563
+ }
5564
+ },
5565
+
5566
+ teardown: function() {
5567
+ jQuery.event.remove( this, "._change" );
5568
+
5569
+ return !rformElems.test( this.nodeName );
5570
+ }
5571
+ };
5572
+ }
5573
+
5574
+ // Create "bubbling" focus and blur events
5575
+ if ( !jQuery.support.focusinBubbles ) {
5576
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
5577
+
5578
+ // Attach a single capturing handler while someone wants focusin/focusout
5579
+ var attaches = 0,
5580
+ handler = function( event ) {
5581
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
5582
+ };
5583
+
5584
+ jQuery.event.special[ fix ] = {
5585
+ setup: function() {
5586
+ if ( attaches++ === 0 ) {
5587
+ document.addEventListener( orig, handler, true );
5588
+ }
5589
+ },
5590
+ teardown: function() {
5591
+ if ( --attaches === 0 ) {
5592
+ document.removeEventListener( orig, handler, true );
5593
+ }
5594
+ }
5595
+ };
5596
+ });
5597
+ }
5598
+
5599
+ jQuery.fn.extend({
5600
+
5601
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
5602
+ var type, origFn;
5603
+
5604
+ // Types can be a map of types/handlers
5605
+ if ( typeof types === "object" ) {
5606
+ // ( types-Object, selector, data )
5607
+ if ( typeof selector !== "string" ) {
5608
+ // ( types-Object, data )
5609
+ data = data || selector;
5610
+ selector = undefined;
5611
+ }
5612
+ for ( type in types ) {
5613
+ this.on( type, selector, data, types[ type ], one );
5614
+ }
5615
+ return this;
5616
+ }
5617
+
5618
+ if ( data == null && fn == null ) {
5619
+ // ( types, fn )
5620
+ fn = selector;
5621
+ data = selector = undefined;
5622
+ } else if ( fn == null ) {
5623
+ if ( typeof selector === "string" ) {
5624
+ // ( types, selector, fn )
5625
+ fn = data;
5626
+ data = undefined;
5627
+ } else {
5628
+ // ( types, data, fn )
5629
+ fn = data;
5630
+ data = selector;
5631
+ selector = undefined;
5632
+ }
5633
+ }
5634
+ if ( fn === false ) {
5635
+ fn = returnFalse;
5636
+ } else if ( !fn ) {
5637
+ return this;
5638
+ }
5639
+
5640
+ if ( one === 1 ) {
5641
+ origFn = fn;
5642
+ fn = function( event ) {
5643
+ // Can use an empty set, since event contains the info
5644
+ jQuery().off( event );
5645
+ return origFn.apply( this, arguments );
5646
+ };
5647
+ // Use same guid so caller can remove using origFn
5648
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
5649
+ }
5650
+ return this.each( function() {
5651
+ jQuery.event.add( this, types, fn, data, selector );
5652
+ });
5653
+ },
5654
+ one: function( types, selector, data, fn ) {
5655
+ return this.on( types, selector, data, fn, 1 );
5656
+ },
5657
+ off: function( types, selector, fn ) {
5658
+ var handleObj, type;
5659
+ if ( types && types.preventDefault && types.handleObj ) {
5660
+ // ( event ) dispatched jQuery.Event
5661
+ handleObj = types.handleObj;
5662
+ jQuery( types.delegateTarget ).off(
5663
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
5664
+ handleObj.selector,
5665
+ handleObj.handler
5666
+ );
5667
+ return this;
5668
+ }
5669
+ if ( typeof types === "object" ) {
5670
+ // ( types-object [, selector] )
5671
+ for ( type in types ) {
5672
+ this.off( type, selector, types[ type ] );
5673
+ }
5674
+ return this;
5675
+ }
5676
+ if ( selector === false || typeof selector === "function" ) {
5677
+ // ( types [, fn] )
5678
+ fn = selector;
5679
+ selector = undefined;
5680
+ }
5681
+ if ( fn === false ) {
5682
+ fn = returnFalse;
5683
+ }
5684
+ return this.each(function() {
5685
+ jQuery.event.remove( this, types, fn, selector );
5686
+ });
5687
+ },
5688
+
5689
+ trigger: function( type, data ) {
5690
+ return this.each(function() {
5691
+ jQuery.event.trigger( type, data, this );
5692
+ });
5693
+ },
5694
+ triggerHandler: function( type, data ) {
5695
+ var elem = this[0];
5696
+ if ( elem ) {
5697
+ return jQuery.event.trigger( type, data, elem, true );
5698
+ }
5699
+ }
5700
+ });
5701
+ var isSimple = /^.[^:#\[\.,]*$/,
5702
+ rparentsprev = /^(?:parents|prev(?:Until|All))/,
5703
+ rneedsContext = jQuery.expr.match.needsContext,
5704
+ // methods guaranteed to produce a unique set when starting from a unique set
5705
+ guaranteedUnique = {
5706
+ children: true,
5707
+ contents: true,
5708
+ next: true,
5709
+ prev: true
5710
+ };
5711
+
5712
+ jQuery.fn.extend({
5713
+ find: function( selector ) {
5714
+ var i,
5715
+ ret = [],
5716
+ self = this,
5717
+ len = self.length;
5718
+
5719
+ if ( typeof selector !== "string" ) {
5720
+ return this.pushStack( jQuery( selector ).filter(function() {
5721
+ for ( i = 0; i < len; i++ ) {
5722
+ if ( jQuery.contains( self[ i ], this ) ) {
5723
+ return true;
5724
+ }
5725
+ }
5726
+ }) );
5727
+ }
5728
+
5729
+ for ( i = 0; i < len; i++ ) {
5730
+ jQuery.find( selector, self[ i ], ret );
5731
+ }
5732
+
5733
+ // Needed because $( selector, context ) becomes $( context ).find( selector )
5734
+ ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
5735
+ ret.selector = this.selector ? this.selector + " " + selector : selector;
5736
+ return ret;
5737
+ },
5738
+
5739
+ has: function( target ) {
5740
+ var i,
5741
+ targets = jQuery( target, this ),
5742
+ len = targets.length;
5743
+
5744
+ return this.filter(function() {
5745
+ for ( i = 0; i < len; i++ ) {
5746
+ if ( jQuery.contains( this, targets[i] ) ) {
5747
+ return true;
5748
+ }
5749
+ }
5750
+ });
5751
+ },
5752
+
5753
+ not: function( selector ) {
5754
+ return this.pushStack( winnow(this, selector || [], true) );
5755
+ },
5756
+
5757
+ filter: function( selector ) {
5758
+ return this.pushStack( winnow(this, selector || [], false) );
5759
+ },
5760
+
5761
+ is: function( selector ) {
5762
+ return !!winnow(
5763
+ this,
5764
+
5765
+ // If this is a positional/relative selector, check membership in the returned set
5766
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
5767
+ typeof selector === "string" && rneedsContext.test( selector ) ?
5768
+ jQuery( selector ) :
5769
+ selector || [],
5770
+ false
5771
+ ).length;
5772
+ },
5773
+
5774
+ closest: function( selectors, context ) {
5775
+ var cur,
5776
+ i = 0,
5777
+ l = this.length,
5778
+ ret = [],
5779
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
5780
+ jQuery( selectors, context || this.context ) :
5781
+ 0;
5782
+
5783
+ for ( ; i < l; i++ ) {
5784
+ for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
5785
+ // Always skip document fragments
5786
+ if ( cur.nodeType < 11 && (pos ?
5787
+ pos.index(cur) > -1 :
5788
+
5789
+ // Don't pass non-elements to Sizzle
5790
+ cur.nodeType === 1 &&
5791
+ jQuery.find.matchesSelector(cur, selectors)) ) {
5792
+
5793
+ cur = ret.push( cur );
5794
+ break;
5795
+ }
5796
+ }
5797
+ }
5798
+
5799
+ return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
5800
+ },
5801
+
5802
+ // Determine the position of an element within
5803
+ // the matched set of elements
5804
+ index: function( elem ) {
5805
+
5806
+ // No argument, return index in parent
5807
+ if ( !elem ) {
5808
+ return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
5809
+ }
5810
+
5811
+ // index in selector
5812
+ if ( typeof elem === "string" ) {
5813
+ return jQuery.inArray( this[0], jQuery( elem ) );
5814
+ }
5815
+
5816
+ // Locate the position of the desired element
5817
+ return jQuery.inArray(
5818
+ // If it receives a jQuery object, the first element is used
5819
+ elem.jquery ? elem[0] : elem, this );
5820
+ },
5821
+
5822
+ add: function( selector, context ) {
5823
+ var set = typeof selector === "string" ?
5824
+ jQuery( selector, context ) :
5825
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
5826
+ all = jQuery.merge( this.get(), set );
5827
+
5828
+ return this.pushStack( jQuery.unique(all) );
5829
+ },
5830
+
5831
+ addBack: function( selector ) {
5832
+ return this.add( selector == null ?
5833
+ this.prevObject : this.prevObject.filter(selector)
5834
+ );
5835
+ }
5836
+ });
5837
+
5838
+ function sibling( cur, dir ) {
5839
+ do {
5840
+ cur = cur[ dir ];
5841
+ } while ( cur && cur.nodeType !== 1 );
5842
+
5843
+ return cur;
5844
+ }
5845
+
5846
+ jQuery.each({
5847
+ parent: function( elem ) {
5848
+ var parent = elem.parentNode;
5849
+ return parent && parent.nodeType !== 11 ? parent : null;
5850
+ },
5851
+ parents: function( elem ) {
5852
+ return jQuery.dir( elem, "parentNode" );
5853
+ },
5854
+ parentsUntil: function( elem, i, until ) {
5855
+ return jQuery.dir( elem, "parentNode", until );
5856
+ },
5857
+ next: function( elem ) {
5858
+ return sibling( elem, "nextSibling" );
5859
+ },
5860
+ prev: function( elem ) {
5861
+ return sibling( elem, "previousSibling" );
5862
+ },
5863
+ nextAll: function( elem ) {
5864
+ return jQuery.dir( elem, "nextSibling" );
5865
+ },
5866
+ prevAll: function( elem ) {
5867
+ return jQuery.dir( elem, "previousSibling" );
5868
+ },
5869
+ nextUntil: function( elem, i, until ) {
5870
+ return jQuery.dir( elem, "nextSibling", until );
5871
+ },
5872
+ prevUntil: function( elem, i, until ) {
5873
+ return jQuery.dir( elem, "previousSibling", until );
5874
+ },
5875
+ siblings: function( elem ) {
5876
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
5877
+ },
5878
+ children: function( elem ) {
5879
+ return jQuery.sibling( elem.firstChild );
5880
+ },
5881
+ contents: function( elem ) {
5882
+ return jQuery.nodeName( elem, "iframe" ) ?
5883
+ elem.contentDocument || elem.contentWindow.document :
5884
+ jQuery.merge( [], elem.childNodes );
5885
+ }
5886
+ }, function( name, fn ) {
5887
+ jQuery.fn[ name ] = function( until, selector ) {
5888
+ var ret = jQuery.map( this, fn, until );
5889
+
5890
+ if ( name.slice( -5 ) !== "Until" ) {
5891
+ selector = until;
5892
+ }
5893
+
5894
+ if ( selector && typeof selector === "string" ) {
5895
+ ret = jQuery.filter( selector, ret );
5896
+ }
5897
+
5898
+ if ( this.length > 1 ) {
5899
+ // Remove duplicates
5900
+ if ( !guaranteedUnique[ name ] ) {
5901
+ ret = jQuery.unique( ret );
5902
+ }
5903
+
5904
+ // Reverse order for parents* and prev-derivatives
5905
+ if ( rparentsprev.test( name ) ) {
5906
+ ret = ret.reverse();
5907
+ }
5908
+ }
5909
+
5910
+ return this.pushStack( ret );
5911
+ };
5912
+ });
5913
+
5914
+ jQuery.extend({
5915
+ filter: function( expr, elems, not ) {
5916
+ var elem = elems[ 0 ];
5917
+
5918
+ if ( not ) {
5919
+ expr = ":not(" + expr + ")";
5920
+ }
5921
+
5922
+ return elems.length === 1 && elem.nodeType === 1 ?
5923
+ jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
5924
+ jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
5925
+ return elem.nodeType === 1;
5926
+ }));
5927
+ },
5928
+
5929
+ dir: function( elem, dir, until ) {
5930
+ var matched = [],
5931
+ cur = elem[ dir ];
5932
+
5933
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
5934
+ if ( cur.nodeType === 1 ) {
5935
+ matched.push( cur );
5936
+ }
5937
+ cur = cur[dir];
5938
+ }
5939
+ return matched;
5940
+ },
5941
+
5942
+ sibling: function( n, elem ) {
5943
+ var r = [];
5944
+
5945
+ for ( ; n; n = n.nextSibling ) {
5946
+ if ( n.nodeType === 1 && n !== elem ) {
5947
+ r.push( n );
5948
+ }
5949
+ }
5950
+
5951
+ return r;
5952
+ }
5953
+ });
5954
+
5955
+ // Implement the identical functionality for filter and not
5956
+ function winnow( elements, qualifier, not ) {
5957
+ if ( jQuery.isFunction( qualifier ) ) {
5958
+ return jQuery.grep( elements, function( elem, i ) {
5959
+ /* jshint -W018 */
5960
+ return !!qualifier.call( elem, i, elem ) !== not;
5961
+ });
5962
+
5963
+ }
5964
+
5965
+ if ( qualifier.nodeType ) {
5966
+ return jQuery.grep( elements, function( elem ) {
5967
+ return ( elem === qualifier ) !== not;
5968
+ });
5969
+
5970
+ }
5971
+
5972
+ if ( typeof qualifier === "string" ) {
5973
+ if ( isSimple.test( qualifier ) ) {
5974
+ return jQuery.filter( qualifier, elements, not );
5975
+ }
5976
+
5977
+ qualifier = jQuery.filter( qualifier, elements );
5978
+ }
5979
+
5980
+ return jQuery.grep( elements, function( elem ) {
5981
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
5982
+ });
5983
+ }
5984
+ function createSafeFragment( document ) {
5985
+ var list = nodeNames.split( "|" ),
5986
+ safeFrag = document.createDocumentFragment();
5987
+
5988
+ if ( safeFrag.createElement ) {
5989
+ while ( list.length ) {
5990
+ safeFrag.createElement(
5991
+ list.pop()
5992
+ );
5993
+ }
5994
+ }
5995
+ return safeFrag;
5996
+ }
5997
+
5998
+ var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
5999
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
6000
+ rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
6001
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
6002
+ rleadingWhitespace = /^\s+/,
6003
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
6004
+ rtagName = /<([\w:]+)/,
6005
+ rtbody = /<tbody/i,
6006
+ rhtml = /<|&#?\w+;/,
6007
+ rnoInnerhtml = /<(?:script|style|link)/i,
6008
+ manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
6009
+ // checked="checked" or checked
6010
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
6011
+ rscriptType = /^$|\/(?:java|ecma)script/i,
6012
+ rscriptTypeMasked = /^true\/(.*)/,
6013
+ rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
6014
+
6015
+ // We have to close these tags to support XHTML (#13200)
6016
+ wrapMap = {
6017
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
6018
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
6019
+ area: [ 1, "<map>", "</map>" ],
6020
+ param: [ 1, "<object>", "</object>" ],
6021
+ thead: [ 1, "<table>", "</table>" ],
6022
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
6023
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
6024
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
6025
+
6026
+ // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
6027
+ // unless wrapped in a div with non-breaking characters in front of it.
6028
+ _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
6029
+ },
6030
+ safeFragment = createSafeFragment( document ),
6031
+ fragmentDiv = safeFragment.appendChild( document.createElement("div") );
6032
+
6033
+ wrapMap.optgroup = wrapMap.option;
6034
+ wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
6035
+ wrapMap.th = wrapMap.td;
6036
+
6037
+ jQuery.fn.extend({
6038
+ text: function( value ) {
6039
+ return jQuery.access( this, function( value ) {
6040
+ return value === undefined ?
6041
+ jQuery.text( this ) :
6042
+ this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
6043
+ }, null, value, arguments.length );
6044
+ },
6045
+
6046
+ append: function() {
6047
+ return this.domManip( arguments, function( elem ) {
6048
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
6049
+ var target = manipulationTarget( this, elem );
6050
+ target.appendChild( elem );
6051
+ }
6052
+ });
6053
+ },
6054
+
6055
+ prepend: function() {
6056
+ return this.domManip( arguments, function( elem ) {
6057
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
6058
+ var target = manipulationTarget( this, elem );
6059
+ target.insertBefore( elem, target.firstChild );
6060
+ }
6061
+ });
6062
+ },
6063
+
6064
+ before: function() {
6065
+ return this.domManip( arguments, function( elem ) {
6066
+ if ( this.parentNode ) {
6067
+ this.parentNode.insertBefore( elem, this );
6068
+ }
6069
+ });
6070
+ },
6071
+
6072
+ after: function() {
6073
+ return this.domManip( arguments, function( elem ) {
6074
+ if ( this.parentNode ) {
6075
+ this.parentNode.insertBefore( elem, this.nextSibling );
6076
+ }
6077
+ });
6078
+ },
6079
+
6080
+ // keepData is for internal use only--do not document
6081
+ remove: function( selector, keepData ) {
6082
+ var elem,
6083
+ elems = selector ? jQuery.filter( selector, this ) : this,
6084
+ i = 0;
6085
+
6086
+ for ( ; (elem = elems[i]) != null; i++ ) {
6087
+
6088
+ if ( !keepData && elem.nodeType === 1 ) {
6089
+ jQuery.cleanData( getAll( elem ) );
6090
+ }
6091
+
6092
+ if ( elem.parentNode ) {
6093
+ if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
6094
+ setGlobalEval( getAll( elem, "script" ) );
6095
+ }
6096
+ elem.parentNode.removeChild( elem );
6097
+ }
6098
+ }
6099
+
6100
+ return this;
6101
+ },
6102
+
6103
+ empty: function() {
6104
+ var elem,
6105
+ i = 0;
6106
+
6107
+ for ( ; (elem = this[i]) != null; i++ ) {
6108
+ // Remove element nodes and prevent memory leaks
6109
+ if ( elem.nodeType === 1 ) {
6110
+ jQuery.cleanData( getAll( elem, false ) );
6111
+ }
6112
+
6113
+ // Remove any remaining nodes
6114
+ while ( elem.firstChild ) {
6115
+ elem.removeChild( elem.firstChild );
6116
+ }
6117
+
6118
+ // If this is a select, ensure that it displays empty (#12336)
6119
+ // Support: IE<9
6120
+ if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
6121
+ elem.options.length = 0;
6122
+ }
6123
+ }
6124
+
6125
+ return this;
6126
+ },
6127
+
6128
+ clone: function( dataAndEvents, deepDataAndEvents ) {
6129
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
6130
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
6131
+
6132
+ return this.map( function () {
6133
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
6134
+ });
6135
+ },
6136
+
6137
+ html: function( value ) {
6138
+ return jQuery.access( this, function( value ) {
6139
+ var elem = this[0] || {},
6140
+ i = 0,
6141
+ l = this.length;
6142
+
6143
+ if ( value === undefined ) {
6144
+ return elem.nodeType === 1 ?
6145
+ elem.innerHTML.replace( rinlinejQuery, "" ) :
6146
+ undefined;
6147
+ }
6148
+
6149
+ // See if we can take a shortcut and just use innerHTML
6150
+ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
6151
+ ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
6152
+ ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
6153
+ !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
6154
+
6155
+ value = value.replace( rxhtmlTag, "<$1></$2>" );
6156
+
6157
+ try {
6158
+ for (; i < l; i++ ) {
6159
+ // Remove element nodes and prevent memory leaks
6160
+ elem = this[i] || {};
6161
+ if ( elem.nodeType === 1 ) {
6162
+ jQuery.cleanData( getAll( elem, false ) );
6163
+ elem.innerHTML = value;
6164
+ }
6165
+ }
6166
+
6167
+ elem = 0;
6168
+
6169
+ // If using innerHTML throws an exception, use the fallback method
6170
+ } catch(e) {}
6171
+ }
6172
+
6173
+ if ( elem ) {
6174
+ this.empty().append( value );
6175
+ }
6176
+ }, null, value, arguments.length );
6177
+ },
6178
+
6179
+ replaceWith: function() {
6180
+ var
6181
+ // Snapshot the DOM in case .domManip sweeps something relevant into its fragment
6182
+ args = jQuery.map( this, function( elem ) {
6183
+ return [ elem.nextSibling, elem.parentNode ];
6184
+ }),
6185
+ i = 0;
6186
+
6187
+ // Make the changes, replacing each context element with the new content
6188
+ this.domManip( arguments, function( elem ) {
6189
+ var next = args[ i++ ],
6190
+ parent = args[ i++ ];
6191
+
6192
+ if ( parent ) {
6193
+ // Don't use the snapshot next if it has moved (#13810)
6194
+ if ( next && next.parentNode !== parent ) {
6195
+ next = this.nextSibling;
6196
+ }
6197
+ jQuery( this ).remove();
6198
+ parent.insertBefore( elem, next );
6199
+ }
6200
+ // Allow new content to include elements from the context set
6201
+ }, true );
6202
+
6203
+ // Force removal if there was no new content (e.g., from empty arguments)
6204
+ return i ? this : this.remove();
6205
+ },
6206
+
6207
+ detach: function( selector ) {
6208
+ return this.remove( selector, true );
6209
+ },
6210
+
6211
+ domManip: function( args, callback, allowIntersection ) {
6212
+
6213
+ // Flatten any nested arrays
6214
+ args = core_concat.apply( [], args );
6215
+
6216
+ var first, node, hasScripts,
6217
+ scripts, doc, fragment,
6218
+ i = 0,
6219
+ l = this.length,
6220
+ set = this,
6221
+ iNoClone = l - 1,
6222
+ value = args[0],
6223
+ isFunction = jQuery.isFunction( value );
6224
+
6225
+ // We can't cloneNode fragments that contain checked, in WebKit
6226
+ if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
6227
+ return this.each(function( index ) {
6228
+ var self = set.eq( index );
6229
+ if ( isFunction ) {
6230
+ args[0] = value.call( this, index, self.html() );
6231
+ }
6232
+ self.domManip( args, callback, allowIntersection );
6233
+ });
6234
+ }
6235
+
6236
+ if ( l ) {
6237
+ fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this );
6238
+ first = fragment.firstChild;
6239
+
6240
+ if ( fragment.childNodes.length === 1 ) {
6241
+ fragment = first;
6242
+ }
6243
+
6244
+ if ( first ) {
6245
+ scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
6246
+ hasScripts = scripts.length;
6247
+
6248
+ // Use the original fragment for the last item instead of the first because it can end up
6249
+ // being emptied incorrectly in certain situations (#8070).
6250
+ for ( ; i < l; i++ ) {
6251
+ node = fragment;
6252
+
6253
+ if ( i !== iNoClone ) {
6254
+ node = jQuery.clone( node, true, true );
6255
+
6256
+ // Keep references to cloned scripts for later restoration
6257
+ if ( hasScripts ) {
6258
+ jQuery.merge( scripts, getAll( node, "script" ) );
6259
+ }
6260
+ }
6261
+
6262
+ callback.call( this[i], node, i );
6263
+ }
6264
+
6265
+ if ( hasScripts ) {
6266
+ doc = scripts[ scripts.length - 1 ].ownerDocument;
6267
+
6268
+ // Reenable scripts
6269
+ jQuery.map( scripts, restoreScript );
6270
+
6271
+ // Evaluate executable scripts on first document insertion
6272
+ for ( i = 0; i < hasScripts; i++ ) {
6273
+ node = scripts[ i ];
6274
+ if ( rscriptType.test( node.type || "" ) &&
6275
+ !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
6276
+
6277
+ if ( node.src ) {
6278
+ // Hope ajax is available...
6279
+ jQuery._evalUrl( node.src );
6280
+ } else {
6281
+ jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
6282
+ }
6283
+ }
6284
+ }
6285
+ }
6286
+
6287
+ // Fix #11809: Avoid leaking memory
6288
+ fragment = first = null;
6289
+ }
6290
+ }
6291
+
6292
+ return this;
6293
+ }
6294
+ });
6295
+
6296
+ // Support: IE<8
6297
+ // Manipulating tables requires a tbody
6298
+ function manipulationTarget( elem, content ) {
6299
+ return jQuery.nodeName( elem, "table" ) &&
6300
+ jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ?
6301
+
6302
+ elem.getElementsByTagName("tbody")[0] ||
6303
+ elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
6304
+ elem;
6305
+ }
6306
+
6307
+ // Replace/restore the type attribute of script elements for safe DOM manipulation
6308
+ function disableScript( elem ) {
6309
+ elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type;
6310
+ return elem;
6311
+ }
6312
+ function restoreScript( elem ) {
6313
+ var match = rscriptTypeMasked.exec( elem.type );
6314
+ if ( match ) {
6315
+ elem.type = match[1];
6316
+ } else {
6317
+ elem.removeAttribute("type");
6318
+ }
6319
+ return elem;
6320
+ }
6321
+
6322
+ // Mark scripts as having already been evaluated
6323
+ function setGlobalEval( elems, refElements ) {
6324
+ var elem,
6325
+ i = 0;
6326
+ for ( ; (elem = elems[i]) != null; i++ ) {
6327
+ jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
6328
+ }
6329
+ }
6330
+
6331
+ function cloneCopyEvent( src, dest ) {
6332
+
6333
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
6334
+ return;
6335
+ }
6336
+
6337
+ var type, i, l,
6338
+ oldData = jQuery._data( src ),
6339
+ curData = jQuery._data( dest, oldData ),
6340
+ events = oldData.events;
6341
+
6342
+ if ( events ) {
6343
+ delete curData.handle;
6344
+ curData.events = {};
6345
+
6346
+ for ( type in events ) {
6347
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
6348
+ jQuery.event.add( dest, type, events[ type ][ i ] );
6349
+ }
6350
+ }
6351
+ }
6352
+
6353
+ // make the cloned public data object a copy from the original
6354
+ if ( curData.data ) {
6355
+ curData.data = jQuery.extend( {}, curData.data );
6356
+ }
6357
+ }
6358
+
6359
+ function fixCloneNodeIssues( src, dest ) {
6360
+ var nodeName, e, data;
6361
+
6362
+ // We do not need to do anything for non-Elements
6363
+ if ( dest.nodeType !== 1 ) {
6364
+ return;
6365
+ }
6366
+
6367
+ nodeName = dest.nodeName.toLowerCase();
6368
+
6369
+ // IE6-8 copies events bound via attachEvent when using cloneNode.
6370
+ if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
6371
+ data = jQuery._data( dest );
6372
+
6373
+ for ( e in data.events ) {
6374
+ jQuery.removeEvent( dest, e, data.handle );
6375
+ }
6376
+
6377
+ // Event data gets referenced instead of copied if the expando gets copied too
6378
+ dest.removeAttribute( jQuery.expando );
6379
+ }
6380
+
6381
+ // IE blanks contents when cloning scripts, and tries to evaluate newly-set text
6382
+ if ( nodeName === "script" && dest.text !== src.text ) {
6383
+ disableScript( dest ).text = src.text;
6384
+ restoreScript( dest );
6385
+
6386
+ // IE6-10 improperly clones children of object elements using classid.
6387
+ // IE10 throws NoModificationAllowedError if parent is null, #12132.
6388
+ } else if ( nodeName === "object" ) {
6389
+ if ( dest.parentNode ) {
6390
+ dest.outerHTML = src.outerHTML;
6391
+ }
6392
+
6393
+ // This path appears unavoidable for IE9. When cloning an object
6394
+ // element in IE9, the outerHTML strategy above is not sufficient.
6395
+ // If the src has innerHTML and the destination does not,
6396
+ // copy the src.innerHTML into the dest.innerHTML. #10324
6397
+ if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
6398
+ dest.innerHTML = src.innerHTML;
6399
+ }
6400
+
6401
+ } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
6402
+ // IE6-8 fails to persist the checked state of a cloned checkbox
6403
+ // or radio button. Worse, IE6-7 fail to give the cloned element
6404
+ // a checked appearance if the defaultChecked value isn't also set
6405
+
6406
+ dest.defaultChecked = dest.checked = src.checked;
6407
+
6408
+ // IE6-7 get confused and end up setting the value of a cloned
6409
+ // checkbox/radio button to an empty string instead of "on"
6410
+ if ( dest.value !== src.value ) {
6411
+ dest.value = src.value;
6412
+ }
6413
+
6414
+ // IE6-8 fails to return the selected option to the default selected
6415
+ // state when cloning options
6416
+ } else if ( nodeName === "option" ) {
6417
+ dest.defaultSelected = dest.selected = src.defaultSelected;
6418
+
6419
+ // IE6-8 fails to set the defaultValue to the correct value when
6420
+ // cloning other types of input fields
6421
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
6422
+ dest.defaultValue = src.defaultValue;
6423
+ }
6424
+ }
6425
+
6426
+ jQuery.each({
6427
+ appendTo: "append",
6428
+ prependTo: "prepend",
6429
+ insertBefore: "before",
6430
+ insertAfter: "after",
6431
+ replaceAll: "replaceWith"
6432
+ }, function( name, original ) {
6433
+ jQuery.fn[ name ] = function( selector ) {
6434
+ var elems,
6435
+ i = 0,
6436
+ ret = [],
6437
+ insert = jQuery( selector ),
6438
+ last = insert.length - 1;
6439
+
6440
+ for ( ; i <= last; i++ ) {
6441
+ elems = i === last ? this : this.clone(true);
6442
+ jQuery( insert[i] )[ original ]( elems );
6443
+
6444
+ // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
6445
+ core_push.apply( ret, elems.get() );
6446
+ }
6447
+
6448
+ return this.pushStack( ret );
6449
+ };
6450
+ });
6451
+
6452
+ function getAll( context, tag ) {
6453
+ var elems, elem,
6454
+ i = 0,
6455
+ found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) :
6456
+ typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) :
6457
+ undefined;
6458
+
6459
+ if ( !found ) {
6460
+ for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
6461
+ if ( !tag || jQuery.nodeName( elem, tag ) ) {
6462
+ found.push( elem );
6463
+ } else {
6464
+ jQuery.merge( found, getAll( elem, tag ) );
6465
+ }
6466
+ }
6467
+ }
6468
+
6469
+ return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
6470
+ jQuery.merge( [ context ], found ) :
6471
+ found;
6472
+ }
6473
+
6474
+ // Used in buildFragment, fixes the defaultChecked property
6475
+ function fixDefaultChecked( elem ) {
6476
+ if ( manipulation_rcheckableType.test( elem.type ) ) {
6477
+ elem.defaultChecked = elem.checked;
6478
+ }
6479
+ }
6480
+
6481
+ jQuery.extend({
6482
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
6483
+ var destElements, node, clone, i, srcElements,
6484
+ inPage = jQuery.contains( elem.ownerDocument, elem );
6485
+
6486
+ if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
6487
+ clone = elem.cloneNode( true );
6488
+
6489
+ // IE<=8 does not properly clone detached, unknown element nodes
6490
+ } else {
6491
+ fragmentDiv.innerHTML = elem.outerHTML;
6492
+ fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
6493
+ }
6494
+
6495
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
6496
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
6497
+
6498
+ // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
6499
+ destElements = getAll( clone );
6500
+ srcElements = getAll( elem );
6501
+
6502
+ // Fix all IE cloning issues
6503
+ for ( i = 0; (node = srcElements[i]) != null; ++i ) {
6504
+ // Ensure that the destination node is not null; Fixes #9587
6505
+ if ( destElements[i] ) {
6506
+ fixCloneNodeIssues( node, destElements[i] );
6507
+ }
6508
+ }
6509
+ }
6510
+
6511
+ // Copy the events from the original to the clone
6512
+ if ( dataAndEvents ) {
6513
+ if ( deepDataAndEvents ) {
6514
+ srcElements = srcElements || getAll( elem );
6515
+ destElements = destElements || getAll( clone );
6516
+
6517
+ for ( i = 0; (node = srcElements[i]) != null; i++ ) {
6518
+ cloneCopyEvent( node, destElements[i] );
6519
+ }
6520
+ } else {
6521
+ cloneCopyEvent( elem, clone );
6522
+ }
6523
+ }
6524
+
6525
+ // Preserve script evaluation history
6526
+ destElements = getAll( clone, "script" );
6527
+ if ( destElements.length > 0 ) {
6528
+ setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
6529
+ }
6530
+
6531
+ destElements = srcElements = node = null;
6532
+
6533
+ // Return the cloned set
6534
+ return clone;
6535
+ },
6536
+
6537
+ buildFragment: function( elems, context, scripts, selection ) {
6538
+ var j, elem, contains,
6539
+ tmp, tag, tbody, wrap,
6540
+ l = elems.length,
6541
+
6542
+ // Ensure a safe fragment
6543
+ safe = createSafeFragment( context ),
6544
+
6545
+ nodes = [],
6546
+ i = 0;
6547
+
6548
+ for ( ; i < l; i++ ) {
6549
+ elem = elems[ i ];
6550
+
6551
+ if ( elem || elem === 0 ) {
6552
+
6553
+ // Add nodes directly
6554
+ if ( jQuery.type( elem ) === "object" ) {
6555
+ jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
6556
+
6557
+ // Convert non-html into a text node
6558
+ } else if ( !rhtml.test( elem ) ) {
6559
+ nodes.push( context.createTextNode( elem ) );
6560
+
6561
+ // Convert html into DOM nodes
6562
+ } else {
6563
+ tmp = tmp || safe.appendChild( context.createElement("div") );
6564
+
6565
+ // Deserialize a standard representation
6566
+ tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
6567
+ wrap = wrapMap[ tag ] || wrapMap._default;
6568
+
6569
+ tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
6570
+
6571
+ // Descend through wrappers to the right content
6572
+ j = wrap[0];
6573
+ while ( j-- ) {
6574
+ tmp = tmp.lastChild;
6575
+ }
6576
+
6577
+ // Manually add leading whitespace removed by IE
6578
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
6579
+ nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
6580
+ }
6581
+
6582
+ // Remove IE's autoinserted <tbody> from table fragments
6583
+ if ( !jQuery.support.tbody ) {
6584
+
6585
+ // String was a <table>, *may* have spurious <tbody>
6586
+ elem = tag === "table" && !rtbody.test( elem ) ?
6587
+ tmp.firstChild :
6588
+
6589
+ // String was a bare <thead> or <tfoot>
6590
+ wrap[1] === "<table>" && !rtbody.test( elem ) ?
6591
+ tmp :
6592
+ 0;
6593
+
6594
+ j = elem && elem.childNodes.length;
6595
+ while ( j-- ) {
6596
+ if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
6597
+ elem.removeChild( tbody );
6598
+ }
6599
+ }
6600
+ }
6601
+
6602
+ jQuery.merge( nodes, tmp.childNodes );
6603
+
6604
+ // Fix #12392 for WebKit and IE > 9
6605
+ tmp.textContent = "";
6606
+
6607
+ // Fix #12392 for oldIE
6608
+ while ( tmp.firstChild ) {
6609
+ tmp.removeChild( tmp.firstChild );
6610
+ }
6611
+
6612
+ // Remember the top-level container for proper cleanup
6613
+ tmp = safe.lastChild;
6614
+ }
6615
+ }
6616
+ }
6617
+
6618
+ // Fix #11356: Clear elements from fragment
6619
+ if ( tmp ) {
6620
+ safe.removeChild( tmp );
6621
+ }
6622
+
6623
+ // Reset defaultChecked for any radios and checkboxes
6624
+ // about to be appended to the DOM in IE 6/7 (#8060)
6625
+ if ( !jQuery.support.appendChecked ) {
6626
+ jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
6627
+ }
6628
+
6629
+ i = 0;
6630
+ while ( (elem = nodes[ i++ ]) ) {
6631
+
6632
+ // #4087 - If origin and destination elements are the same, and this is
6633
+ // that element, do not do anything
6634
+ if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
6635
+ continue;
6636
+ }
6637
+
6638
+ contains = jQuery.contains( elem.ownerDocument, elem );
6639
+
6640
+ // Append to fragment
6641
+ tmp = getAll( safe.appendChild( elem ), "script" );
6642
+
6643
+ // Preserve script evaluation history
6644
+ if ( contains ) {
6645
+ setGlobalEval( tmp );
6646
+ }
6647
+
6648
+ // Capture executables
6649
+ if ( scripts ) {
6650
+ j = 0;
6651
+ while ( (elem = tmp[ j++ ]) ) {
6652
+ if ( rscriptType.test( elem.type || "" ) ) {
6653
+ scripts.push( elem );
6654
+ }
6655
+ }
6656
+ }
6657
+ }
6658
+
6659
+ tmp = null;
6660
+
6661
+ return safe;
6662
+ },
6663
+
6664
+ cleanData: function( elems, /* internal */ acceptData ) {
6665
+ var elem, type, id, data,
6666
+ i = 0,
6667
+ internalKey = jQuery.expando,
6668
+ cache = jQuery.cache,
6669
+ deleteExpando = jQuery.support.deleteExpando,
6670
+ special = jQuery.event.special;
6671
+
6672
+ for ( ; (elem = elems[i]) != null; i++ ) {
6673
+
6674
+ if ( acceptData || jQuery.acceptData( elem ) ) {
6675
+
6676
+ id = elem[ internalKey ];
6677
+ data = id && cache[ id ];
6678
+
6679
+ if ( data ) {
6680
+ if ( data.events ) {
6681
+ for ( type in data.events ) {
6682
+ if ( special[ type ] ) {
6683
+ jQuery.event.remove( elem, type );
6684
+
6685
+ // This is a shortcut to avoid jQuery.event.remove's overhead
6686
+ } else {
6687
+ jQuery.removeEvent( elem, type, data.handle );
6688
+ }
6689
+ }
6690
+ }
6691
+
6692
+ // Remove cache only if it was not already removed by jQuery.event.remove
6693
+ if ( cache[ id ] ) {
6694
+
6695
+ delete cache[ id ];
6696
+
6697
+ // IE does not allow us to delete expando properties from nodes,
6698
+ // nor does it have a removeAttribute function on Document nodes;
6699
+ // we must handle all of these cases
6700
+ if ( deleteExpando ) {
6701
+ delete elem[ internalKey ];
6702
+
6703
+ } else if ( typeof elem.removeAttribute !== core_strundefined ) {
6704
+ elem.removeAttribute( internalKey );
6705
+
6706
+ } else {
6707
+ elem[ internalKey ] = null;
6708
+ }
6709
+
6710
+ core_deletedIds.push( id );
6711
+ }
6712
+ }
6713
+ }
6714
+ }
6715
+ },
6716
+
6717
+ _evalUrl: function( url ) {
6718
+ return jQuery.ajax({
6719
+ url: url,
6720
+ type: "GET",
6721
+ dataType: "script",
6722
+ async: false,
6723
+ global: false,
6724
+ "throws": true
6725
+ });
6726
+ }
6727
+ });
6728
+ jQuery.fn.extend({
6729
+ wrapAll: function( html ) {
6730
+ if ( jQuery.isFunction( html ) ) {
6731
+ return this.each(function(i) {
6732
+ jQuery(this).wrapAll( html.call(this, i) );
6733
+ });
6734
+ }
6735
+
6736
+ if ( this[0] ) {
6737
+ // The elements to wrap the target around
6738
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
6739
+
6740
+ if ( this[0].parentNode ) {
6741
+ wrap.insertBefore( this[0] );
6742
+ }
6743
+
6744
+ wrap.map(function() {
6745
+ var elem = this;
6746
+
6747
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
6748
+ elem = elem.firstChild;
6749
+ }
6750
+
6751
+ return elem;
6752
+ }).append( this );
6753
+ }
6754
+
6755
+ return this;
6756
+ },
6757
+
6758
+ wrapInner: function( html ) {
6759
+ if ( jQuery.isFunction( html ) ) {
6760
+ return this.each(function(i) {
6761
+ jQuery(this).wrapInner( html.call(this, i) );
6762
+ });
6763
+ }
6764
+
6765
+ return this.each(function() {
6766
+ var self = jQuery( this ),
6767
+ contents = self.contents();
6768
+
6769
+ if ( contents.length ) {
6770
+ contents.wrapAll( html );
6771
+
6772
+ } else {
6773
+ self.append( html );
6774
+ }
6775
+ });
6776
+ },
6777
+
6778
+ wrap: function( html ) {
6779
+ var isFunction = jQuery.isFunction( html );
6780
+
6781
+ return this.each(function(i) {
6782
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
6783
+ });
6784
+ },
6785
+
6786
+ unwrap: function() {
6787
+ return this.parent().each(function() {
6788
+ if ( !jQuery.nodeName( this, "body" ) ) {
6789
+ jQuery( this ).replaceWith( this.childNodes );
6790
+ }
6791
+ }).end();
6792
+ }
6793
+ });
6794
+ var iframe, getStyles, curCSS,
6795
+ ralpha = /alpha\([^)]*\)/i,
6796
+ ropacity = /opacity\s*=\s*([^)]*)/,
6797
+ rposition = /^(top|right|bottom|left)$/,
6798
+ // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
6799
+ // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
6800
+ rdisplayswap = /^(none|table(?!-c[ea]).+)/,
6801
+ rmargin = /^margin/,
6802
+ rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
6803
+ rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
6804
+ rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
6805
+ elemdisplay = { BODY: "block" },
6806
+
6807
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
6808
+ cssNormalTransform = {
6809
+ letterSpacing: 0,
6810
+ fontWeight: 400
6811
+ },
6812
+
6813
+ cssExpand = [ "Top", "Right", "Bottom", "Left" ],
6814
+ cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
6815
+
6816
+ // return a css property mapped to a potentially vendor prefixed property
6817
+ function vendorPropName( style, name ) {
6818
+
6819
+ // shortcut for names that are not vendor prefixed
6820
+ if ( name in style ) {
6821
+ return name;
6822
+ }
6823
+
6824
+ // check for vendor prefixed names
6825
+ var capName = name.charAt(0).toUpperCase() + name.slice(1),
6826
+ origName = name,
6827
+ i = cssPrefixes.length;
6828
+
6829
+ while ( i-- ) {
6830
+ name = cssPrefixes[ i ] + capName;
6831
+ if ( name in style ) {
6832
+ return name;
6833
+ }
6834
+ }
6835
+
6836
+ return origName;
6837
+ }
6838
+
6839
+ function isHidden( elem, el ) {
6840
+ // isHidden might be called from jQuery#filter function;
6841
+ // in that case, element will be second argument
6842
+ elem = el || elem;
6843
+ return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
6844
+ }
6845
+
6846
+ function showHide( elements, show ) {
6847
+ var display, elem, hidden,
6848
+ values = [],
6849
+ index = 0,
6850
+ length = elements.length;
6851
+
6852
+ for ( ; index < length; index++ ) {
6853
+ elem = elements[ index ];
6854
+ if ( !elem.style ) {
6855
+ continue;
6856
+ }
6857
+
6858
+ values[ index ] = jQuery._data( elem, "olddisplay" );
6859
+ display = elem.style.display;
6860
+ if ( show ) {
6861
+ // Reset the inline display of this element to learn if it is
6862
+ // being hidden by cascaded rules or not
6863
+ if ( !values[ index ] && display === "none" ) {
6864
+ elem.style.display = "";
6865
+ }
6866
+
6867
+ // Set elements which have been overridden with display: none
6868
+ // in a stylesheet to whatever the default browser style is
6869
+ // for such an element
6870
+ if ( elem.style.display === "" && isHidden( elem ) ) {
6871
+ values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
6872
+ }
6873
+ } else {
6874
+
6875
+ if ( !values[ index ] ) {
6876
+ hidden = isHidden( elem );
6877
+
6878
+ if ( display && display !== "none" || !hidden ) {
6879
+ jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
6880
+ }
6881
+ }
6882
+ }
6883
+ }
6884
+
6885
+ // Set the display of most of the elements in a second loop
6886
+ // to avoid the constant reflow
6887
+ for ( index = 0; index < length; index++ ) {
6888
+ elem = elements[ index ];
6889
+ if ( !elem.style ) {
6890
+ continue;
6891
+ }
6892
+ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
6893
+ elem.style.display = show ? values[ index ] || "" : "none";
6894
+ }
6895
+ }
6896
+
6897
+ return elements;
6898
+ }
6899
+
6900
+ jQuery.fn.extend({
6901
+ css: function( name, value ) {
6902
+ return jQuery.access( this, function( elem, name, value ) {
6903
+ var len, styles,
6904
+ map = {},
6905
+ i = 0;
6906
+
6907
+ if ( jQuery.isArray( name ) ) {
6908
+ styles = getStyles( elem );
6909
+ len = name.length;
6910
+
6911
+ for ( ; i < len; i++ ) {
6912
+ map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
6913
+ }
6914
+
6915
+ return map;
6916
+ }
6917
+
6918
+ return value !== undefined ?
6919
+ jQuery.style( elem, name, value ) :
6920
+ jQuery.css( elem, name );
6921
+ }, name, value, arguments.length > 1 );
6922
+ },
6923
+ show: function() {
6924
+ return showHide( this, true );
6925
+ },
6926
+ hide: function() {
6927
+ return showHide( this );
6928
+ },
6929
+ toggle: function( state ) {
6930
+ if ( typeof state === "boolean" ) {
6931
+ return state ? this.show() : this.hide();
6932
+ }
6933
+
6934
+ return this.each(function() {
6935
+ if ( isHidden( this ) ) {
6936
+ jQuery( this ).show();
6937
+ } else {
6938
+ jQuery( this ).hide();
6939
+ }
6940
+ });
6941
+ }
6942
+ });
6943
+
6944
+ jQuery.extend({
6945
+ // Add in style property hooks for overriding the default
6946
+ // behavior of getting and setting a style property
6947
+ cssHooks: {
6948
+ opacity: {
6949
+ get: function( elem, computed ) {
6950
+ if ( computed ) {
6951
+ // We should always get a number back from opacity
6952
+ var ret = curCSS( elem, "opacity" );
6953
+ return ret === "" ? "1" : ret;
6954
+ }
6955
+ }
6956
+ }
6957
+ },
6958
+
6959
+ // Don't automatically add "px" to these possibly-unitless properties
6960
+ cssNumber: {
6961
+ "columnCount": true,
6962
+ "fillOpacity": true,
6963
+ "fontWeight": true,
6964
+ "lineHeight": true,
6965
+ "opacity": true,
6966
+ "order": true,
6967
+ "orphans": true,
6968
+ "widows": true,
6969
+ "zIndex": true,
6970
+ "zoom": true
6971
+ },
6972
+
6973
+ // Add in properties whose names you wish to fix before
6974
+ // setting or getting the value
6975
+ cssProps: {
6976
+ // normalize float css property
6977
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
6978
+ },
6979
+
6980
+ // Get and set the style property on a DOM Node
6981
+ style: function( elem, name, value, extra ) {
6982
+ // Don't set styles on text and comment nodes
6983
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
6984
+ return;
6985
+ }
6986
+
6987
+ // Make sure that we're working with the right name
6988
+ var ret, type, hooks,
6989
+ origName = jQuery.camelCase( name ),
6990
+ style = elem.style;
6991
+
6992
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
6993
+
6994
+ // gets hook for the prefixed version
6995
+ // followed by the unprefixed version
6996
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
6997
+
6998
+ // Check if we're setting a value
6999
+ if ( value !== undefined ) {
7000
+ type = typeof value;
7001
+
7002
+ // convert relative number strings (+= or -=) to relative numbers. #7345
7003
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
7004
+ value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
7005
+ // Fixes bug #9237
7006
+ type = "number";
7007
+ }
7008
+
7009
+ // Make sure that NaN and null values aren't set. See: #7116
7010
+ if ( value == null || type === "number" && isNaN( value ) ) {
7011
+ return;
7012
+ }
7013
+
7014
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
7015
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
7016
+ value += "px";
7017
+ }
7018
+
7019
+ // Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
7020
+ // but it would mean to define eight (for every problematic property) identical functions
7021
+ if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
7022
+ style[ name ] = "inherit";
7023
+ }
7024
+
7025
+ // If a hook was provided, use that value, otherwise just set the specified value
7026
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
7027
+
7028
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
7029
+ // Fixes bug #5509
7030
+ try {
7031
+ style[ name ] = value;
7032
+ } catch(e) {}
7033
+ }
7034
+
7035
+ } else {
7036
+ // If a hook was provided get the non-computed value from there
7037
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
7038
+ return ret;
7039
+ }
7040
+
7041
+ // Otherwise just get the value from the style object
7042
+ return style[ name ];
7043
+ }
7044
+ },
7045
+
7046
+ css: function( elem, name, extra, styles ) {
7047
+ var num, val, hooks,
7048
+ origName = jQuery.camelCase( name );
7049
+
7050
+ // Make sure that we're working with the right name
7051
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
7052
+
7053
+ // gets hook for the prefixed version
7054
+ // followed by the unprefixed version
7055
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
7056
+
7057
+ // If a hook was provided get the computed value from there
7058
+ if ( hooks && "get" in hooks ) {
7059
+ val = hooks.get( elem, true, extra );
7060
+ }
7061
+
7062
+ // Otherwise, if a way to get the computed value exists, use that
7063
+ if ( val === undefined ) {
7064
+ val = curCSS( elem, name, styles );
7065
+ }
7066
+
7067
+ //convert "normal" to computed value
7068
+ if ( val === "normal" && name in cssNormalTransform ) {
7069
+ val = cssNormalTransform[ name ];
7070
+ }
7071
+
7072
+ // Return, converting to number if forced or a qualifier was provided and val looks numeric
7073
+ if ( extra === "" || extra ) {
7074
+ num = parseFloat( val );
7075
+ return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
7076
+ }
7077
+ return val;
7078
+ }
7079
+ });
7080
+
7081
+ // NOTE: we've included the "window" in window.getComputedStyle
7082
+ // because jsdom on node.js will break without it.
7083
+ if ( window.getComputedStyle ) {
7084
+ getStyles = function( elem ) {
7085
+ return window.getComputedStyle( elem, null );
7086
+ };
7087
+
7088
+ curCSS = function( elem, name, _computed ) {
7089
+ var width, minWidth, maxWidth,
7090
+ computed = _computed || getStyles( elem ),
7091
+
7092
+ // getPropertyValue is only needed for .css('filter') in IE9, see #12537
7093
+ ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
7094
+ style = elem.style;
7095
+
7096
+ if ( computed ) {
7097
+
7098
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
7099
+ ret = jQuery.style( elem, name );
7100
+ }
7101
+
7102
+ // A tribute to the "awesome hack by Dean Edwards"
7103
+ // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
7104
+ // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
7105
+ // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
7106
+ if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
7107
+
7108
+ // Remember the original values
7109
+ width = style.width;
7110
+ minWidth = style.minWidth;
7111
+ maxWidth = style.maxWidth;
7112
+
7113
+ // Put in the new values to get a computed value out
7114
+ style.minWidth = style.maxWidth = style.width = ret;
7115
+ ret = computed.width;
7116
+
7117
+ // Revert the changed values
7118
+ style.width = width;
7119
+ style.minWidth = minWidth;
7120
+ style.maxWidth = maxWidth;
7121
+ }
7122
+ }
7123
+
7124
+ return ret;
7125
+ };
7126
+ } else if ( document.documentElement.currentStyle ) {
7127
+ getStyles = function( elem ) {
7128
+ return elem.currentStyle;
7129
+ };
7130
+
7131
+ curCSS = function( elem, name, _computed ) {
7132
+ var left, rs, rsLeft,
7133
+ computed = _computed || getStyles( elem ),
7134
+ ret = computed ? computed[ name ] : undefined,
7135
+ style = elem.style;
7136
+
7137
+ // Avoid setting ret to empty string here
7138
+ // so we don't default to auto
7139
+ if ( ret == null && style && style[ name ] ) {
7140
+ ret = style[ name ];
7141
+ }
7142
+
7143
+ // From the awesome hack by Dean Edwards
7144
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
7145
+
7146
+ // If we're not dealing with a regular pixel number
7147
+ // but a number that has a weird ending, we need to convert it to pixels
7148
+ // but not position css attributes, as those are proportional to the parent element instead
7149
+ // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
7150
+ if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
7151
+
7152
+ // Remember the original values
7153
+ left = style.left;
7154
+ rs = elem.runtimeStyle;
7155
+ rsLeft = rs && rs.left;
7156
+
7157
+ // Put in the new values to get a computed value out
7158
+ if ( rsLeft ) {
7159
+ rs.left = elem.currentStyle.left;
7160
+ }
7161
+ style.left = name === "fontSize" ? "1em" : ret;
7162
+ ret = style.pixelLeft + "px";
7163
+
7164
+ // Revert the changed values
7165
+ style.left = left;
7166
+ if ( rsLeft ) {
7167
+ rs.left = rsLeft;
7168
+ }
7169
+ }
7170
+
7171
+ return ret === "" ? "auto" : ret;
7172
+ };
7173
+ }
7174
+
7175
+ function setPositiveNumber( elem, value, subtract ) {
7176
+ var matches = rnumsplit.exec( value );
7177
+ return matches ?
7178
+ // Guard against undefined "subtract", e.g., when used as in cssHooks
7179
+ Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
7180
+ value;
7181
+ }
7182
+
7183
+ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
7184
+ var i = extra === ( isBorderBox ? "border" : "content" ) ?
7185
+ // If we already have the right measurement, avoid augmentation
7186
+ 4 :
7187
+ // Otherwise initialize for horizontal or vertical properties
7188
+ name === "width" ? 1 : 0,
7189
+
7190
+ val = 0;
7191
+
7192
+ for ( ; i < 4; i += 2 ) {
7193
+ // both box models exclude margin, so add it if we want it
7194
+ if ( extra === "margin" ) {
7195
+ val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
7196
+ }
7197
+
7198
+ if ( isBorderBox ) {
7199
+ // border-box includes padding, so remove it if we want content
7200
+ if ( extra === "content" ) {
7201
+ val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7202
+ }
7203
+
7204
+ // at this point, extra isn't border nor margin, so remove border
7205
+ if ( extra !== "margin" ) {
7206
+ val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7207
+ }
7208
+ } else {
7209
+ // at this point, extra isn't content, so add padding
7210
+ val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7211
+
7212
+ // at this point, extra isn't content nor padding, so add border
7213
+ if ( extra !== "padding" ) {
7214
+ val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7215
+ }
7216
+ }
7217
+ }
7218
+
7219
+ return val;
7220
+ }
7221
+
7222
+ function getWidthOrHeight( elem, name, extra ) {
7223
+
7224
+ // Start with offset property, which is equivalent to the border-box value
7225
+ var valueIsBorderBox = true,
7226
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
7227
+ styles = getStyles( elem ),
7228
+ isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
7229
+
7230
+ // some non-html elements return undefined for offsetWidth, so check for null/undefined
7231
+ // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
7232
+ // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
7233
+ if ( val <= 0 || val == null ) {
7234
+ // Fall back to computed then uncomputed css if necessary
7235
+ val = curCSS( elem, name, styles );
7236
+ if ( val < 0 || val == null ) {
7237
+ val = elem.style[ name ];
7238
+ }
7239
+
7240
+ // Computed unit is not pixels. Stop here and return.
7241
+ if ( rnumnonpx.test(val) ) {
7242
+ return val;
7243
+ }
7244
+
7245
+ // we need the check for style in case a browser which returns unreliable values
7246
+ // for getComputedStyle silently falls back to the reliable elem.style
7247
+ valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
7248
+
7249
+ // Normalize "", auto, and prepare for extra
7250
+ val = parseFloat( val ) || 0;
7251
+ }
7252
+
7253
+ // use the active box-sizing model to add/subtract irrelevant styles
7254
+ return ( val +
7255
+ augmentWidthOrHeight(
7256
+ elem,
7257
+ name,
7258
+ extra || ( isBorderBox ? "border" : "content" ),
7259
+ valueIsBorderBox,
7260
+ styles
7261
+ )
7262
+ ) + "px";
7263
+ }
7264
+
7265
+ // Try to determine the default display value of an element
7266
+ function css_defaultDisplay( nodeName ) {
7267
+ var doc = document,
7268
+ display = elemdisplay[ nodeName ];
7269
+
7270
+ if ( !display ) {
7271
+ display = actualDisplay( nodeName, doc );
7272
+
7273
+ // If the simple way fails, read from inside an iframe
7274
+ if ( display === "none" || !display ) {
7275
+ // Use the already-created iframe if possible
7276
+ iframe = ( iframe ||
7277
+ jQuery("<iframe frameborder='0' width='0' height='0'/>")
7278
+ .css( "cssText", "display:block !important" )
7279
+ ).appendTo( doc.documentElement );
7280
+
7281
+ // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
7282
+ doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
7283
+ doc.write("<!doctype html><html><body>");
7284
+ doc.close();
7285
+
7286
+ display = actualDisplay( nodeName, doc );
7287
+ iframe.detach();
7288
+ }
7289
+
7290
+ // Store the correct default display
7291
+ elemdisplay[ nodeName ] = display;
7292
+ }
7293
+
7294
+ return display;
7295
+ }
7296
+
7297
+ // Called ONLY from within css_defaultDisplay
7298
+ function actualDisplay( name, doc ) {
7299
+ var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
7300
+ display = jQuery.css( elem[0], "display" );
7301
+ elem.remove();
7302
+ return display;
7303
+ }
7304
+
7305
+ jQuery.each([ "height", "width" ], function( i, name ) {
7306
+ jQuery.cssHooks[ name ] = {
7307
+ get: function( elem, computed, extra ) {
7308
+ if ( computed ) {
7309
+ // certain elements can have dimension info if we invisibly show them
7310
+ // however, it must have a current display style that would benefit from this
7311
+ return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
7312
+ jQuery.swap( elem, cssShow, function() {
7313
+ return getWidthOrHeight( elem, name, extra );
7314
+ }) :
7315
+ getWidthOrHeight( elem, name, extra );
7316
+ }
7317
+ },
7318
+
7319
+ set: function( elem, value, extra ) {
7320
+ var styles = extra && getStyles( elem );
7321
+ return setPositiveNumber( elem, value, extra ?
7322
+ augmentWidthOrHeight(
7323
+ elem,
7324
+ name,
7325
+ extra,
7326
+ jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
7327
+ styles
7328
+ ) : 0
7329
+ );
7330
+ }
7331
+ };
7332
+ });
7333
+
7334
+ if ( !jQuery.support.opacity ) {
7335
+ jQuery.cssHooks.opacity = {
7336
+ get: function( elem, computed ) {
7337
+ // IE uses filters for opacity
7338
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
7339
+ ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
7340
+ computed ? "1" : "";
7341
+ },
7342
+
7343
+ set: function( elem, value ) {
7344
+ var style = elem.style,
7345
+ currentStyle = elem.currentStyle,
7346
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
7347
+ filter = currentStyle && currentStyle.filter || style.filter || "";
7348
+
7349
+ // IE has trouble with opacity if it does not have layout
7350
+ // Force it by setting the zoom level
7351
+ style.zoom = 1;
7352
+
7353
+ // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
7354
+ // if value === "", then remove inline opacity #12685
7355
+ if ( ( value >= 1 || value === "" ) &&
7356
+ jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
7357
+ style.removeAttribute ) {
7358
+
7359
+ // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
7360
+ // if "filter:" is present at all, clearType is disabled, we want to avoid this
7361
+ // style.removeAttribute is IE Only, but so apparently is this code path...
7362
+ style.removeAttribute( "filter" );
7363
+
7364
+ // if there is no filter style applied in a css rule or unset inline opacity, we are done
7365
+ if ( value === "" || currentStyle && !currentStyle.filter ) {
7366
+ return;
7367
+ }
7368
+ }
7369
+
7370
+ // otherwise, set new filter values
7371
+ style.filter = ralpha.test( filter ) ?
7372
+ filter.replace( ralpha, opacity ) :
7373
+ filter + " " + opacity;
7374
+ }
7375
+ };
7376
+ }
7377
+
7378
+ // These hooks cannot be added until DOM ready because the support test
7379
+ // for it is not run until after DOM ready
7380
+ jQuery(function() {
7381
+ if ( !jQuery.support.reliableMarginRight ) {
7382
+ jQuery.cssHooks.marginRight = {
7383
+ get: function( elem, computed ) {
7384
+ if ( computed ) {
7385
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
7386
+ // Work around by temporarily setting element display to inline-block
7387
+ return jQuery.swap( elem, { "display": "inline-block" },
7388
+ curCSS, [ elem, "marginRight" ] );
7389
+ }
7390
+ }
7391
+ };
7392
+ }
7393
+
7394
+ // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
7395
+ // getComputedStyle returns percent when specified for top/left/bottom/right
7396
+ // rather than make the css module depend on the offset module, we just check for it here
7397
+ if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
7398
+ jQuery.each( [ "top", "left" ], function( i, prop ) {
7399
+ jQuery.cssHooks[ prop ] = {
7400
+ get: function( elem, computed ) {
7401
+ if ( computed ) {
7402
+ computed = curCSS( elem, prop );
7403
+ // if curCSS returns percentage, fallback to offset
7404
+ return rnumnonpx.test( computed ) ?
7405
+ jQuery( elem ).position()[ prop ] + "px" :
7406
+ computed;
7407
+ }
7408
+ }
7409
+ };
7410
+ });
7411
+ }
7412
+
7413
+ });
7414
+
7415
+ if ( jQuery.expr && jQuery.expr.filters ) {
7416
+ jQuery.expr.filters.hidden = function( elem ) {
7417
+ // Support: Opera <= 12.12
7418
+ // Opera reports offsetWidths and offsetHeights less than zero on some elements
7419
+ return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
7420
+ (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
7421
+ };
7422
+
7423
+ jQuery.expr.filters.visible = function( elem ) {
7424
+ return !jQuery.expr.filters.hidden( elem );
7425
+ };
7426
+ }
7427
+
7428
+ // These hooks are used by animate to expand properties
7429
+ jQuery.each({
7430
+ margin: "",
7431
+ padding: "",
7432
+ border: "Width"
7433
+ }, function( prefix, suffix ) {
7434
+ jQuery.cssHooks[ prefix + suffix ] = {
7435
+ expand: function( value ) {
7436
+ var i = 0,
7437
+ expanded = {},
7438
+
7439
+ // assumes a single number if not a string
7440
+ parts = typeof value === "string" ? value.split(" ") : [ value ];
7441
+
7442
+ for ( ; i < 4; i++ ) {
7443
+ expanded[ prefix + cssExpand[ i ] + suffix ] =
7444
+ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
7445
+ }
7446
+
7447
+ return expanded;
7448
+ }
7449
+ };
7450
+
7451
+ if ( !rmargin.test( prefix ) ) {
7452
+ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
7453
+ }
7454
+ });
7455
+ var r20 = /%20/g,
7456
+ rbracket = /\[\]$/,
7457
+ rCRLF = /\r?\n/g,
7458
+ rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
7459
+ rsubmittable = /^(?:input|select|textarea|keygen)/i;
7460
+
7461
+ jQuery.fn.extend({
7462
+ serialize: function() {
7463
+ return jQuery.param( this.serializeArray() );
7464
+ },
7465
+ serializeArray: function() {
7466
+ return this.map(function(){
7467
+ // Can add propHook for "elements" to filter or add form elements
7468
+ var elements = jQuery.prop( this, "elements" );
7469
+ return elements ? jQuery.makeArray( elements ) : this;
7470
+ })
7471
+ .filter(function(){
7472
+ var type = this.type;
7473
+ // Use .is(":disabled") so that fieldset[disabled] works
7474
+ return this.name && !jQuery( this ).is( ":disabled" ) &&
7475
+ rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
7476
+ ( this.checked || !manipulation_rcheckableType.test( type ) );
7477
+ })
7478
+ .map(function( i, elem ){
7479
+ var val = jQuery( this ).val();
7480
+
7481
+ return val == null ?
7482
+ null :
7483
+ jQuery.isArray( val ) ?
7484
+ jQuery.map( val, function( val ){
7485
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7486
+ }) :
7487
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7488
+ }).get();
7489
+ }
7490
+ });
7491
+
7492
+ //Serialize an array of form elements or a set of
7493
+ //key/values into a query string
7494
+ jQuery.param = function( a, traditional ) {
7495
+ var prefix,
7496
+ s = [],
7497
+ add = function( key, value ) {
7498
+ // If value is a function, invoke it and return its value
7499
+ value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
7500
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
7501
+ };
7502
+
7503
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
7504
+ if ( traditional === undefined ) {
7505
+ traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
7506
+ }
7507
+
7508
+ // If an array was passed in, assume that it is an array of form elements.
7509
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
7510
+ // Serialize the form elements
7511
+ jQuery.each( a, function() {
7512
+ add( this.name, this.value );
7513
+ });
7514
+
7515
+ } else {
7516
+ // If traditional, encode the "old" way (the way 1.3.2 or older
7517
+ // did it), otherwise encode params recursively.
7518
+ for ( prefix in a ) {
7519
+ buildParams( prefix, a[ prefix ], traditional, add );
7520
+ }
7521
+ }
7522
+
7523
+ // Return the resulting serialization
7524
+ return s.join( "&" ).replace( r20, "+" );
7525
+ };
7526
+
7527
+ function buildParams( prefix, obj, traditional, add ) {
7528
+ var name;
7529
+
7530
+ if ( jQuery.isArray( obj ) ) {
7531
+ // Serialize array item.
7532
+ jQuery.each( obj, function( i, v ) {
7533
+ if ( traditional || rbracket.test( prefix ) ) {
7534
+ // Treat each array item as a scalar.
7535
+ add( prefix, v );
7536
+
7537
+ } else {
7538
+ // Item is non-scalar (array or object), encode its numeric index.
7539
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
7540
+ }
7541
+ });
7542
+
7543
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
7544
+ // Serialize object item.
7545
+ for ( name in obj ) {
7546
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
7547
+ }
7548
+
7549
+ } else {
7550
+ // Serialize scalar item.
7551
+ add( prefix, obj );
7552
+ }
7553
+ }
7554
+ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
7555
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
7556
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
7557
+
7558
+ // Handle event binding
7559
+ jQuery.fn[ name ] = function( data, fn ) {
7560
+ return arguments.length > 0 ?
7561
+ this.on( name, null, data, fn ) :
7562
+ this.trigger( name );
7563
+ };
7564
+ });
7565
+
7566
+ jQuery.fn.extend({
7567
+ hover: function( fnOver, fnOut ) {
7568
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
7569
+ },
7570
+
7571
+ bind: function( types, data, fn ) {
7572
+ return this.on( types, null, data, fn );
7573
+ },
7574
+ unbind: function( types, fn ) {
7575
+ return this.off( types, null, fn );
7576
+ },
7577
+
7578
+ delegate: function( selector, types, data, fn ) {
7579
+ return this.on( types, selector, data, fn );
7580
+ },
7581
+ undelegate: function( selector, types, fn ) {
7582
+ // ( namespace ) or ( selector, types [, fn] )
7583
+ return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
7584
+ }
7585
+ });
7586
+ var
7587
+ // Document location
7588
+ ajaxLocParts,
7589
+ ajaxLocation,
7590
+ ajax_nonce = jQuery.now(),
7591
+
7592
+ ajax_rquery = /\?/,
7593
+ rhash = /#.*$/,
7594
+ rts = /([?&])_=[^&]*/,
7595
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
7596
+ // #7653, #8125, #8152: local protocol detection
7597
+ rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
7598
+ rnoContent = /^(?:GET|HEAD)$/,
7599
+ rprotocol = /^\/\//,
7600
+ rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
7601
+
7602
+ // Keep a copy of the old load method
7603
+ _load = jQuery.fn.load,
7604
+
7605
+ /* Prefilters
7606
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
7607
+ * 2) These are called:
7608
+ * - BEFORE asking for a transport
7609
+ * - AFTER param serialization (s.data is a string if s.processData is true)
7610
+ * 3) key is the dataType
7611
+ * 4) the catchall symbol "*" can be used
7612
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
7613
+ */
7614
+ prefilters = {},
7615
+
7616
+ /* Transports bindings
7617
+ * 1) key is the dataType
7618
+ * 2) the catchall symbol "*" can be used
7619
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
7620
+ */
7621
+ transports = {},
7622
+
7623
+ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
7624
+ allTypes = "*/".concat("*");
7625
+
7626
+ // #8138, IE may throw an exception when accessing
7627
+ // a field from window.location if document.domain has been set
7628
+ try {
7629
+ ajaxLocation = location.href;
7630
+ } catch( e ) {
7631
+ // Use the href attribute of an A element
7632
+ // since IE will modify it given document.location
7633
+ ajaxLocation = document.createElement( "a" );
7634
+ ajaxLocation.href = "";
7635
+ ajaxLocation = ajaxLocation.href;
7636
+ }
7637
+
7638
+ // Segment location into parts
7639
+ ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
7640
+
7641
+ // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
7642
+ function addToPrefiltersOrTransports( structure ) {
7643
+
7644
+ // dataTypeExpression is optional and defaults to "*"
7645
+ return function( dataTypeExpression, func ) {
7646
+
7647
+ if ( typeof dataTypeExpression !== "string" ) {
7648
+ func = dataTypeExpression;
7649
+ dataTypeExpression = "*";
7650
+ }
7651
+
7652
+ var dataType,
7653
+ i = 0,
7654
+ dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
7655
+
7656
+ if ( jQuery.isFunction( func ) ) {
7657
+ // For each dataType in the dataTypeExpression
7658
+ while ( (dataType = dataTypes[i++]) ) {
7659
+ // Prepend if requested
7660
+ if ( dataType[0] === "+" ) {
7661
+ dataType = dataType.slice( 1 ) || "*";
7662
+ (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
7663
+
7664
+ // Otherwise append
7665
+ } else {
7666
+ (structure[ dataType ] = structure[ dataType ] || []).push( func );
7667
+ }
7668
+ }
7669
+ }
7670
+ };
7671
+ }
7672
+
7673
+ // Base inspection function for prefilters and transports
7674
+ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
7675
+
7676
+ var inspected = {},
7677
+ seekingTransport = ( structure === transports );
7678
+
7679
+ function inspect( dataType ) {
7680
+ var selected;
7681
+ inspected[ dataType ] = true;
7682
+ jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
7683
+ var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
7684
+ if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
7685
+ options.dataTypes.unshift( dataTypeOrTransport );
7686
+ inspect( dataTypeOrTransport );
7687
+ return false;
7688
+ } else if ( seekingTransport ) {
7689
+ return !( selected = dataTypeOrTransport );
7690
+ }
7691
+ });
7692
+ return selected;
7693
+ }
7694
+
7695
+ return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
7696
+ }
7697
+
7698
+ // A special extend for ajax options
7699
+ // that takes "flat" options (not to be deep extended)
7700
+ // Fixes #9887
7701
+ function ajaxExtend( target, src ) {
7702
+ var deep, key,
7703
+ flatOptions = jQuery.ajaxSettings.flatOptions || {};
7704
+
7705
+ for ( key in src ) {
7706
+ if ( src[ key ] !== undefined ) {
7707
+ ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
7708
+ }
7709
+ }
7710
+ if ( deep ) {
7711
+ jQuery.extend( true, target, deep );
7712
+ }
7713
+
7714
+ return target;
7715
+ }
7716
+
7717
+ jQuery.fn.load = function( url, params, callback ) {
7718
+ if ( typeof url !== "string" && _load ) {
7719
+ return _load.apply( this, arguments );
7720
+ }
7721
+
7722
+ var selector, response, type,
7723
+ self = this,
7724
+ off = url.indexOf(" ");
7725
+
7726
+ if ( off >= 0 ) {
7727
+ selector = url.slice( off, url.length );
7728
+ url = url.slice( 0, off );
7729
+ }
7730
+
7731
+ // If it's a function
7732
+ if ( jQuery.isFunction( params ) ) {
7733
+
7734
+ // We assume that it's the callback
7735
+ callback = params;
7736
+ params = undefined;
7737
+
7738
+ // Otherwise, build a param string
7739
+ } else if ( params && typeof params === "object" ) {
7740
+ type = "POST";
7741
+ }
7742
+
7743
+ // If we have elements to modify, make the request
7744
+ if ( self.length > 0 ) {
7745
+ jQuery.ajax({
7746
+ url: url,
7747
+
7748
+ // if "type" variable is undefined, then "GET" method will be used
7749
+ type: type,
7750
+ dataType: "html",
7751
+ data: params
7752
+ }).done(function( responseText ) {
7753
+
7754
+ // Save response for use in complete callback
7755
+ response = arguments;
7756
+
7757
+ self.html( selector ?
7758
+
7759
+ // If a selector was specified, locate the right elements in a dummy div
7760
+ // Exclude scripts to avoid IE 'Permission Denied' errors
7761
+ jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
7762
+
7763
+ // Otherwise use the full result
7764
+ responseText );
7765
+
7766
+ }).complete( callback && function( jqXHR, status ) {
7767
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
7768
+ });
7769
+ }
7770
+
7771
+ return this;
7772
+ };
7773
+
7774
+ // Attach a bunch of functions for handling common AJAX events
7775
+ jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
7776
+ jQuery.fn[ type ] = function( fn ){
7777
+ return this.on( type, fn );
7778
+ };
7779
+ });
7780
+
7781
+ jQuery.extend({
7782
+
7783
+ // Counter for holding the number of active queries
7784
+ active: 0,
7785
+
7786
+ // Last-Modified header cache for next request
7787
+ lastModified: {},
7788
+ etag: {},
7789
+
7790
+ ajaxSettings: {
7791
+ url: ajaxLocation,
7792
+ type: "GET",
7793
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
7794
+ global: true,
7795
+ processData: true,
7796
+ async: true,
7797
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
7798
+ /*
7799
+ timeout: 0,
7800
+ data: null,
7801
+ dataType: null,
7802
+ username: null,
7803
+ password: null,
7804
+ cache: null,
7805
+ throws: false,
7806
+ traditional: false,
7807
+ headers: {},
7808
+ */
7809
+
7810
+ accepts: {
7811
+ "*": allTypes,
7812
+ text: "text/plain",
7813
+ html: "text/html",
7814
+ xml: "application/xml, text/xml",
7815
+ json: "application/json, text/javascript"
7816
+ },
7817
+
7818
+ contents: {
7819
+ xml: /xml/,
7820
+ html: /html/,
7821
+ json: /json/
7822
+ },
7823
+
7824
+ responseFields: {
7825
+ xml: "responseXML",
7826
+ text: "responseText",
7827
+ json: "responseJSON"
7828
+ },
7829
+
7830
+ // Data converters
7831
+ // Keys separate source (or catchall "*") and destination types with a single space
7832
+ converters: {
7833
+
7834
+ // Convert anything to text
7835
+ "* text": String,
7836
+
7837
+ // Text to html (true = no transformation)
7838
+ "text html": true,
7839
+
7840
+ // Evaluate text as a json expression
7841
+ "text json": jQuery.parseJSON,
7842
+
7843
+ // Parse text as xml
7844
+ "text xml": jQuery.parseXML
7845
+ },
7846
+
7847
+ // For options that shouldn't be deep extended:
7848
+ // you can add your own custom options here if
7849
+ // and when you create one that shouldn't be
7850
+ // deep extended (see ajaxExtend)
7851
+ flatOptions: {
7852
+ url: true,
7853
+ context: true
7854
+ }
7855
+ },
7856
+
7857
+ // Creates a full fledged settings object into target
7858
+ // with both ajaxSettings and settings fields.
7859
+ // If target is omitted, writes into ajaxSettings.
7860
+ ajaxSetup: function( target, settings ) {
7861
+ return settings ?
7862
+
7863
+ // Building a settings object
7864
+ ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
7865
+
7866
+ // Extending ajaxSettings
7867
+ ajaxExtend( jQuery.ajaxSettings, target );
7868
+ },
7869
+
7870
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
7871
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
7872
+
7873
+ // Main method
7874
+ ajax: function( url, options ) {
7875
+
7876
+ // If url is an object, simulate pre-1.5 signature
7877
+ if ( typeof url === "object" ) {
7878
+ options = url;
7879
+ url = undefined;
7880
+ }
7881
+
7882
+ // Force options to be an object
7883
+ options = options || {};
7884
+
7885
+ var // Cross-domain detection vars
7886
+ parts,
7887
+ // Loop variable
7888
+ i,
7889
+ // URL without anti-cache param
7890
+ cacheURL,
7891
+ // Response headers as string
7892
+ responseHeadersString,
7893
+ // timeout handle
7894
+ timeoutTimer,
7895
+
7896
+ // To know if global events are to be dispatched
7897
+ fireGlobals,
7898
+
7899
+ transport,
7900
+ // Response headers
7901
+ responseHeaders,
7902
+ // Create the final options object
7903
+ s = jQuery.ajaxSetup( {}, options ),
7904
+ // Callbacks context
7905
+ callbackContext = s.context || s,
7906
+ // Context for global events is callbackContext if it is a DOM node or jQuery collection
7907
+ globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
7908
+ jQuery( callbackContext ) :
7909
+ jQuery.event,
7910
+ // Deferreds
7911
+ deferred = jQuery.Deferred(),
7912
+ completeDeferred = jQuery.Callbacks("once memory"),
7913
+ // Status-dependent callbacks
7914
+ statusCode = s.statusCode || {},
7915
+ // Headers (they are sent all at once)
7916
+ requestHeaders = {},
7917
+ requestHeadersNames = {},
7918
+ // The jqXHR state
7919
+ state = 0,
7920
+ // Default abort message
7921
+ strAbort = "canceled",
7922
+ // Fake xhr
7923
+ jqXHR = {
7924
+ readyState: 0,
7925
+
7926
+ // Builds headers hashtable if needed
7927
+ getResponseHeader: function( key ) {
7928
+ var match;
7929
+ if ( state === 2 ) {
7930
+ if ( !responseHeaders ) {
7931
+ responseHeaders = {};
7932
+ while ( (match = rheaders.exec( responseHeadersString )) ) {
7933
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
7934
+ }
7935
+ }
7936
+ match = responseHeaders[ key.toLowerCase() ];
7937
+ }
7938
+ return match == null ? null : match;
7939
+ },
7940
+
7941
+ // Raw string
7942
+ getAllResponseHeaders: function() {
7943
+ return state === 2 ? responseHeadersString : null;
7944
+ },
7945
+
7946
+ // Caches the header
7947
+ setRequestHeader: function( name, value ) {
7948
+ var lname = name.toLowerCase();
7949
+ if ( !state ) {
7950
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
7951
+ requestHeaders[ name ] = value;
7952
+ }
7953
+ return this;
7954
+ },
7955
+
7956
+ // Overrides response content-type header
7957
+ overrideMimeType: function( type ) {
7958
+ if ( !state ) {
7959
+ s.mimeType = type;
7960
+ }
7961
+ return this;
7962
+ },
7963
+
7964
+ // Status-dependent callbacks
7965
+ statusCode: function( map ) {
7966
+ var code;
7967
+ if ( map ) {
7968
+ if ( state < 2 ) {
7969
+ for ( code in map ) {
7970
+ // Lazy-add the new callback in a way that preserves old ones
7971
+ statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
7972
+ }
7973
+ } else {
7974
+ // Execute the appropriate callbacks
7975
+ jqXHR.always( map[ jqXHR.status ] );
7976
+ }
7977
+ }
7978
+ return this;
7979
+ },
7980
+
7981
+ // Cancel the request
7982
+ abort: function( statusText ) {
7983
+ var finalText = statusText || strAbort;
7984
+ if ( transport ) {
7985
+ transport.abort( finalText );
7986
+ }
7987
+ done( 0, finalText );
7988
+ return this;
7989
+ }
7990
+ };
7991
+
7992
+ // Attach deferreds
7993
+ deferred.promise( jqXHR ).complete = completeDeferred.add;
7994
+ jqXHR.success = jqXHR.done;
7995
+ jqXHR.error = jqXHR.fail;
7996
+
7997
+ // Remove hash character (#7531: and string promotion)
7998
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
7999
+ // Handle falsy url in the settings object (#10093: consistency with old signature)
8000
+ // We also use the url parameter if available
8001
+ s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
8002
+
8003
+ // Alias method option to type as per ticket #12004
8004
+ s.type = options.method || options.type || s.method || s.type;
8005
+
8006
+ // Extract dataTypes list
8007
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
8008
+
8009
+ // A cross-domain request is in order when we have a protocol:host:port mismatch
8010
+ if ( s.crossDomain == null ) {
8011
+ parts = rurl.exec( s.url.toLowerCase() );
8012
+ s.crossDomain = !!( parts &&
8013
+ ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
8014
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
8015
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
8016
+ );
8017
+ }
8018
+
8019
+ // Convert data if not already a string
8020
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
8021
+ s.data = jQuery.param( s.data, s.traditional );
8022
+ }
8023
+
8024
+ // Apply prefilters
8025
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
8026
+
8027
+ // If request was aborted inside a prefilter, stop there
8028
+ if ( state === 2 ) {
8029
+ return jqXHR;
8030
+ }
8031
+
8032
+ // We can fire global events as of now if asked to
8033
+ fireGlobals = s.global;
8034
+
8035
+ // Watch for a new set of requests
8036
+ if ( fireGlobals && jQuery.active++ === 0 ) {
8037
+ jQuery.event.trigger("ajaxStart");
8038
+ }
8039
+
8040
+ // Uppercase the type
8041
+ s.type = s.type.toUpperCase();
8042
+
8043
+ // Determine if request has content
8044
+ s.hasContent = !rnoContent.test( s.type );
8045
+
8046
+ // Save the URL in case we're toying with the If-Modified-Since
8047
+ // and/or If-None-Match header later on
8048
+ cacheURL = s.url;
8049
+
8050
+ // More options handling for requests with no content
8051
+ if ( !s.hasContent ) {
8052
+
8053
+ // If data is available, append data to url
8054
+ if ( s.data ) {
8055
+ cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
8056
+ // #9682: remove data so that it's not used in an eventual retry
8057
+ delete s.data;
8058
+ }
8059
+
8060
+ // Add anti-cache in url if needed
8061
+ if ( s.cache === false ) {
8062
+ s.url = rts.test( cacheURL ) ?
8063
+
8064
+ // If there is already a '_' parameter, set its value
8065
+ cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
8066
+
8067
+ // Otherwise add one to the end
8068
+ cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
8069
+ }
8070
+ }
8071
+
8072
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8073
+ if ( s.ifModified ) {
8074
+ if ( jQuery.lastModified[ cacheURL ] ) {
8075
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
8076
+ }
8077
+ if ( jQuery.etag[ cacheURL ] ) {
8078
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
8079
+ }
8080
+ }
8081
+
8082
+ // Set the correct header, if data is being sent
8083
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
8084
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
8085
+ }
8086
+
8087
+ // Set the Accepts header for the server, depending on the dataType
8088
+ jqXHR.setRequestHeader(
8089
+ "Accept",
8090
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
8091
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
8092
+ s.accepts[ "*" ]
8093
+ );
8094
+
8095
+ // Check for headers option
8096
+ for ( i in s.headers ) {
8097
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
8098
+ }
8099
+
8100
+ // Allow custom headers/mimetypes and early abort
8101
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
8102
+ // Abort if not done already and return
8103
+ return jqXHR.abort();
8104
+ }
8105
+
8106
+ // aborting is no longer a cancellation
8107
+ strAbort = "abort";
8108
+
8109
+ // Install callbacks on deferreds
8110
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
8111
+ jqXHR[ i ]( s[ i ] );
8112
+ }
8113
+
8114
+ // Get transport
8115
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
8116
+
8117
+ // If no transport, we auto-abort
8118
+ if ( !transport ) {
8119
+ done( -1, "No Transport" );
8120
+ } else {
8121
+ jqXHR.readyState = 1;
8122
+
8123
+ // Send global event
8124
+ if ( fireGlobals ) {
8125
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
8126
+ }
8127
+ // Timeout
8128
+ if ( s.async && s.timeout > 0 ) {
8129
+ timeoutTimer = setTimeout(function() {
8130
+ jqXHR.abort("timeout");
8131
+ }, s.timeout );
8132
+ }
8133
+
8134
+ try {
8135
+ state = 1;
8136
+ transport.send( requestHeaders, done );
8137
+ } catch ( e ) {
8138
+ // Propagate exception as error if not done
8139
+ if ( state < 2 ) {
8140
+ done( -1, e );
8141
+ // Simply rethrow otherwise
8142
+ } else {
8143
+ throw e;
8144
+ }
8145
+ }
8146
+ }
8147
+
8148
+ // Callback for when everything is done
8149
+ function done( status, nativeStatusText, responses, headers ) {
8150
+ var isSuccess, success, error, response, modified,
8151
+ statusText = nativeStatusText;
8152
+
8153
+ // Called once
8154
+ if ( state === 2 ) {
8155
+ return;
8156
+ }
8157
+
8158
+ // State is "done" now
8159
+ state = 2;
8160
+
8161
+ // Clear timeout if it exists
8162
+ if ( timeoutTimer ) {
8163
+ clearTimeout( timeoutTimer );
8164
+ }
8165
+
8166
+ // Dereference transport for early garbage collection
8167
+ // (no matter how long the jqXHR object will be used)
8168
+ transport = undefined;
8169
+
8170
+ // Cache response headers
8171
+ responseHeadersString = headers || "";
8172
+
8173
+ // Set readyState
8174
+ jqXHR.readyState = status > 0 ? 4 : 0;
8175
+
8176
+ // Determine if successful
8177
+ isSuccess = status >= 200 && status < 300 || status === 304;
8178
+
8179
+ // Get response data
8180
+ if ( responses ) {
8181
+ response = ajaxHandleResponses( s, jqXHR, responses );
8182
+ }
8183
+
8184
+ // Convert no matter what (that way responseXXX fields are always set)
8185
+ response = ajaxConvert( s, response, jqXHR, isSuccess );
8186
+
8187
+ // If successful, handle type chaining
8188
+ if ( isSuccess ) {
8189
+
8190
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8191
+ if ( s.ifModified ) {
8192
+ modified = jqXHR.getResponseHeader("Last-Modified");
8193
+ if ( modified ) {
8194
+ jQuery.lastModified[ cacheURL ] = modified;
8195
+ }
8196
+ modified = jqXHR.getResponseHeader("etag");
8197
+ if ( modified ) {
8198
+ jQuery.etag[ cacheURL ] = modified;
8199
+ }
8200
+ }
8201
+
8202
+ // if no content
8203
+ if ( status === 204 || s.type === "HEAD" ) {
8204
+ statusText = "nocontent";
8205
+
8206
+ // if not modified
8207
+ } else if ( status === 304 ) {
8208
+ statusText = "notmodified";
8209
+
8210
+ // If we have data, let's convert it
8211
+ } else {
8212
+ statusText = response.state;
8213
+ success = response.data;
8214
+ error = response.error;
8215
+ isSuccess = !error;
8216
+ }
8217
+ } else {
8218
+ // We extract error from statusText
8219
+ // then normalize statusText and status for non-aborts
8220
+ error = statusText;
8221
+ if ( status || !statusText ) {
8222
+ statusText = "error";
8223
+ if ( status < 0 ) {
8224
+ status = 0;
8225
+ }
8226
+ }
8227
+ }
8228
+
8229
+ // Set data for the fake xhr object
8230
+ jqXHR.status = status;
8231
+ jqXHR.statusText = ( nativeStatusText || statusText ) + "";
8232
+
8233
+ // Success/Error
8234
+ if ( isSuccess ) {
8235
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
8236
+ } else {
8237
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
8238
+ }
8239
+
8240
+ // Status-dependent callbacks
8241
+ jqXHR.statusCode( statusCode );
8242
+ statusCode = undefined;
8243
+
8244
+ if ( fireGlobals ) {
8245
+ globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
8246
+ [ jqXHR, s, isSuccess ? success : error ] );
8247
+ }
8248
+
8249
+ // Complete
8250
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
8251
+
8252
+ if ( fireGlobals ) {
8253
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
8254
+ // Handle the global AJAX counter
8255
+ if ( !( --jQuery.active ) ) {
8256
+ jQuery.event.trigger("ajaxStop");
8257
+ }
8258
+ }
8259
+ }
8260
+
8261
+ return jqXHR;
8262
+ },
8263
+
8264
+ getJSON: function( url, data, callback ) {
8265
+ return jQuery.get( url, data, callback, "json" );
8266
+ },
8267
+
8268
+ getScript: function( url, callback ) {
8269
+ return jQuery.get( url, undefined, callback, "script" );
8270
+ }
8271
+ });
8272
+
8273
+ jQuery.each( [ "get", "post" ], function( i, method ) {
8274
+ jQuery[ method ] = function( url, data, callback, type ) {
8275
+ // shift arguments if data argument was omitted
8276
+ if ( jQuery.isFunction( data ) ) {
8277
+ type = type || callback;
8278
+ callback = data;
8279
+ data = undefined;
8280
+ }
8281
+
8282
+ return jQuery.ajax({
8283
+ url: url,
8284
+ type: method,
8285
+ dataType: type,
8286
+ data: data,
8287
+ success: callback
8288
+ });
8289
+ };
8290
+ });
8291
+
8292
+ /* Handles responses to an ajax request:
8293
+ * - finds the right dataType (mediates between content-type and expected dataType)
8294
+ * - returns the corresponding response
8295
+ */
8296
+ function ajaxHandleResponses( s, jqXHR, responses ) {
8297
+ var firstDataType, ct, finalDataType, type,
8298
+ contents = s.contents,
8299
+ dataTypes = s.dataTypes;
8300
+
8301
+ // Remove auto dataType and get content-type in the process
8302
+ while( dataTypes[ 0 ] === "*" ) {
8303
+ dataTypes.shift();
8304
+ if ( ct === undefined ) {
8305
+ ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
8306
+ }
8307
+ }
8308
+
8309
+ // Check if we're dealing with a known content-type
8310
+ if ( ct ) {
8311
+ for ( type in contents ) {
8312
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
8313
+ dataTypes.unshift( type );
8314
+ break;
8315
+ }
8316
+ }
8317
+ }
8318
+
8319
+ // Check to see if we have a response for the expected dataType
8320
+ if ( dataTypes[ 0 ] in responses ) {
8321
+ finalDataType = dataTypes[ 0 ];
8322
+ } else {
8323
+ // Try convertible dataTypes
8324
+ for ( type in responses ) {
8325
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
8326
+ finalDataType = type;
8327
+ break;
8328
+ }
8329
+ if ( !firstDataType ) {
8330
+ firstDataType = type;
8331
+ }
8332
+ }
8333
+ // Or just use first one
8334
+ finalDataType = finalDataType || firstDataType;
8335
+ }
8336
+
8337
+ // If we found a dataType
8338
+ // We add the dataType to the list if needed
8339
+ // and return the corresponding response
8340
+ if ( finalDataType ) {
8341
+ if ( finalDataType !== dataTypes[ 0 ] ) {
8342
+ dataTypes.unshift( finalDataType );
8343
+ }
8344
+ return responses[ finalDataType ];
8345
+ }
8346
+ }
8347
+
8348
+ /* Chain conversions given the request and the original response
8349
+ * Also sets the responseXXX fields on the jqXHR instance
8350
+ */
8351
+ function ajaxConvert( s, response, jqXHR, isSuccess ) {
8352
+ var conv2, current, conv, tmp, prev,
8353
+ converters = {},
8354
+ // Work with a copy of dataTypes in case we need to modify it for conversion
8355
+ dataTypes = s.dataTypes.slice();
8356
+
8357
+ // Create converters map with lowercased keys
8358
+ if ( dataTypes[ 1 ] ) {
8359
+ for ( conv in s.converters ) {
8360
+ converters[ conv.toLowerCase() ] = s.converters[ conv ];
8361
+ }
8362
+ }
8363
+
8364
+ current = dataTypes.shift();
8365
+
8366
+ // Convert to each sequential dataType
8367
+ while ( current ) {
8368
+
8369
+ if ( s.responseFields[ current ] ) {
8370
+ jqXHR[ s.responseFields[ current ] ] = response;
8371
+ }
8372
+
8373
+ // Apply the dataFilter if provided
8374
+ if ( !prev && isSuccess && s.dataFilter ) {
8375
+ response = s.dataFilter( response, s.dataType );
8376
+ }
8377
+
8378
+ prev = current;
8379
+ current = dataTypes.shift();
8380
+
8381
+ if ( current ) {
8382
+
8383
+ // There's only work to do if current dataType is non-auto
8384
+ if ( current === "*" ) {
8385
+
8386
+ current = prev;
8387
+
8388
+ // Convert response if prev dataType is non-auto and differs from current
8389
+ } else if ( prev !== "*" && prev !== current ) {
8390
+
8391
+ // Seek a direct converter
8392
+ conv = converters[ prev + " " + current ] || converters[ "* " + current ];
8393
+
8394
+ // If none found, seek a pair
8395
+ if ( !conv ) {
8396
+ for ( conv2 in converters ) {
8397
+
8398
+ // If conv2 outputs current
8399
+ tmp = conv2.split( " " );
8400
+ if ( tmp[ 1 ] === current ) {
8401
+
8402
+ // If prev can be converted to accepted input
8403
+ conv = converters[ prev + " " + tmp[ 0 ] ] ||
8404
+ converters[ "* " + tmp[ 0 ] ];
8405
+ if ( conv ) {
8406
+ // Condense equivalence converters
8407
+ if ( conv === true ) {
8408
+ conv = converters[ conv2 ];
8409
+
8410
+ // Otherwise, insert the intermediate dataType
8411
+ } else if ( converters[ conv2 ] !== true ) {
8412
+ current = tmp[ 0 ];
8413
+ dataTypes.unshift( tmp[ 1 ] );
8414
+ }
8415
+ break;
8416
+ }
8417
+ }
8418
+ }
8419
+ }
8420
+
8421
+ // Apply converter (if not an equivalence)
8422
+ if ( conv !== true ) {
8423
+
8424
+ // Unless errors are allowed to bubble, catch and return them
8425
+ if ( conv && s[ "throws" ] ) {
8426
+ response = conv( response );
8427
+ } else {
8428
+ try {
8429
+ response = conv( response );
8430
+ } catch ( e ) {
8431
+ return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
8432
+ }
8433
+ }
8434
+ }
8435
+ }
8436
+ }
8437
+ }
8438
+
8439
+ return { state: "success", data: response };
8440
+ }
8441
+ // Install script dataType
8442
+ jQuery.ajaxSetup({
8443
+ accepts: {
8444
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
8445
+ },
8446
+ contents: {
8447
+ script: /(?:java|ecma)script/
8448
+ },
8449
+ converters: {
8450
+ "text script": function( text ) {
8451
+ jQuery.globalEval( text );
8452
+ return text;
8453
+ }
8454
+ }
8455
+ });
8456
+
8457
+ // Handle cache's special case and global
8458
+ jQuery.ajaxPrefilter( "script", function( s ) {
8459
+ if ( s.cache === undefined ) {
8460
+ s.cache = false;
8461
+ }
8462
+ if ( s.crossDomain ) {
8463
+ s.type = "GET";
8464
+ s.global = false;
8465
+ }
8466
+ });
8467
+
8468
+ // Bind script tag hack transport
8469
+ jQuery.ajaxTransport( "script", function(s) {
8470
+
8471
+ // This transport only deals with cross domain requests
8472
+ if ( s.crossDomain ) {
8473
+
8474
+ var script,
8475
+ head = document.head || jQuery("head")[0] || document.documentElement;
8476
+
8477
+ return {
8478
+
8479
+ send: function( _, callback ) {
8480
+
8481
+ script = document.createElement("script");
8482
+
8483
+ script.async = true;
8484
+
8485
+ if ( s.scriptCharset ) {
8486
+ script.charset = s.scriptCharset;
8487
+ }
8488
+
8489
+ script.src = s.url;
8490
+
8491
+ // Attach handlers for all browsers
8492
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
8493
+
8494
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
8495
+
8496
+ // Handle memory leak in IE
8497
+ script.onload = script.onreadystatechange = null;
8498
+
8499
+ // Remove the script
8500
+ if ( script.parentNode ) {
8501
+ script.parentNode.removeChild( script );
8502
+ }
8503
+
8504
+ // Dereference the script
8505
+ script = null;
8506
+
8507
+ // Callback if not abort
8508
+ if ( !isAbort ) {
8509
+ callback( 200, "success" );
8510
+ }
8511
+ }
8512
+ };
8513
+
8514
+ // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
8515
+ // Use native DOM manipulation to avoid our domManip AJAX trickery
8516
+ head.insertBefore( script, head.firstChild );
8517
+ },
8518
+
8519
+ abort: function() {
8520
+ if ( script ) {
8521
+ script.onload( undefined, true );
8522
+ }
8523
+ }
8524
+ };
8525
+ }
8526
+ });
8527
+ var oldCallbacks = [],
8528
+ rjsonp = /(=)\?(?=&|$)|\?\?/;
8529
+
8530
+ // Default jsonp settings
8531
+ jQuery.ajaxSetup({
8532
+ jsonp: "callback",
8533
+ jsonpCallback: function() {
8534
+ var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
8535
+ this[ callback ] = true;
8536
+ return callback;
8537
+ }
8538
+ });
8539
+
8540
+ // Detect, normalize options and install callbacks for jsonp requests
8541
+ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
8542
+
8543
+ var callbackName, overwritten, responseContainer,
8544
+ jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
8545
+ "url" :
8546
+ typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
8547
+ );
8548
+
8549
+ // Handle iff the expected data type is "jsonp" or we have a parameter to set
8550
+ if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
8551
+
8552
+ // Get callback name, remembering preexisting value associated with it
8553
+ callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
8554
+ s.jsonpCallback() :
8555
+ s.jsonpCallback;
8556
+
8557
+ // Insert callback into url or form data
8558
+ if ( jsonProp ) {
8559
+ s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
8560
+ } else if ( s.jsonp !== false ) {
8561
+ s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
8562
+ }
8563
+
8564
+ // Use data converter to retrieve json after script execution
8565
+ s.converters["script json"] = function() {
8566
+ if ( !responseContainer ) {
8567
+ jQuery.error( callbackName + " was not called" );
8568
+ }
8569
+ return responseContainer[ 0 ];
8570
+ };
8571
+
8572
+ // force json dataType
8573
+ s.dataTypes[ 0 ] = "json";
8574
+
8575
+ // Install callback
8576
+ overwritten = window[ callbackName ];
8577
+ window[ callbackName ] = function() {
8578
+ responseContainer = arguments;
8579
+ };
8580
+
8581
+ // Clean-up function (fires after converters)
8582
+ jqXHR.always(function() {
8583
+ // Restore preexisting value
8584
+ window[ callbackName ] = overwritten;
8585
+
8586
+ // Save back as free
8587
+ if ( s[ callbackName ] ) {
8588
+ // make sure that re-using the options doesn't screw things around
8589
+ s.jsonpCallback = originalSettings.jsonpCallback;
8590
+
8591
+ // save the callback name for future use
8592
+ oldCallbacks.push( callbackName );
8593
+ }
8594
+
8595
+ // Call if it was a function and we have a response
8596
+ if ( responseContainer && jQuery.isFunction( overwritten ) ) {
8597
+ overwritten( responseContainer[ 0 ] );
8598
+ }
8599
+
8600
+ responseContainer = overwritten = undefined;
8601
+ });
8602
+
8603
+ // Delegate to script
8604
+ return "script";
8605
+ }
8606
+ });
8607
+ var xhrCallbacks, xhrSupported,
8608
+ xhrId = 0,
8609
+ // #5280: Internet Explorer will keep connections alive if we don't abort on unload
8610
+ xhrOnUnloadAbort = window.ActiveXObject && function() {
8611
+ // Abort all pending requests
8612
+ var key;
8613
+ for ( key in xhrCallbacks ) {
8614
+ xhrCallbacks[ key ]( undefined, true );
8615
+ }
8616
+ };
8617
+
8618
+ // Functions to create xhrs
8619
+ function createStandardXHR() {
8620
+ try {
8621
+ return new window.XMLHttpRequest();
8622
+ } catch( e ) {}
8623
+ }
8624
+
8625
+ function createActiveXHR() {
8626
+ try {
8627
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
8628
+ } catch( e ) {}
8629
+ }
8630
+
8631
+ // Create the request object
8632
+ // (This is still attached to ajaxSettings for backward compatibility)
8633
+ jQuery.ajaxSettings.xhr = window.ActiveXObject ?
8634
+ /* Microsoft failed to properly
8635
+ * implement the XMLHttpRequest in IE7 (can't request local files),
8636
+ * so we use the ActiveXObject when it is available
8637
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
8638
+ * we need a fallback.
8639
+ */
8640
+ function() {
8641
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
8642
+ } :
8643
+ // For all other browsers, use the standard XMLHttpRequest object
8644
+ createStandardXHR;
8645
+
8646
+ // Determine support properties
8647
+ xhrSupported = jQuery.ajaxSettings.xhr();
8648
+ jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
8649
+ xhrSupported = jQuery.support.ajax = !!xhrSupported;
8650
+
8651
+ // Create transport if the browser can provide an xhr
8652
+ if ( xhrSupported ) {
8653
+
8654
+ jQuery.ajaxTransport(function( s ) {
8655
+ // Cross domain only allowed if supported through XMLHttpRequest
8656
+ if ( !s.crossDomain || jQuery.support.cors ) {
8657
+
8658
+ var callback;
8659
+
8660
+ return {
8661
+ send: function( headers, complete ) {
8662
+
8663
+ // Get a new xhr
8664
+ var handle, i,
8665
+ xhr = s.xhr();
8666
+
8667
+ // Open the socket
8668
+ // Passing null username, generates a login popup on Opera (#2865)
8669
+ if ( s.username ) {
8670
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
8671
+ } else {
8672
+ xhr.open( s.type, s.url, s.async );
8673
+ }
8674
+
8675
+ // Apply custom fields if provided
8676
+ if ( s.xhrFields ) {
8677
+ for ( i in s.xhrFields ) {
8678
+ xhr[ i ] = s.xhrFields[ i ];
8679
+ }
8680
+ }
8681
+
8682
+ // Override mime type if needed
8683
+ if ( s.mimeType && xhr.overrideMimeType ) {
8684
+ xhr.overrideMimeType( s.mimeType );
8685
+ }
8686
+
8687
+ // X-Requested-With header
8688
+ // For cross-domain requests, seeing as conditions for a preflight are
8689
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
8690
+ // (it can always be set on a per-request basis or even using ajaxSetup)
8691
+ // For same-domain requests, won't change header if already provided.
8692
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
8693
+ headers["X-Requested-With"] = "XMLHttpRequest";
8694
+ }
8695
+
8696
+ // Need an extra try/catch for cross domain requests in Firefox 3
8697
+ try {
8698
+ for ( i in headers ) {
8699
+ xhr.setRequestHeader( i, headers[ i ] );
8700
+ }
8701
+ } catch( err ) {}
8702
+
8703
+ // Do send the request
8704
+ // This may raise an exception which is actually
8705
+ // handled in jQuery.ajax (so no try/catch here)
8706
+ xhr.send( ( s.hasContent && s.data ) || null );
8707
+
8708
+ // Listener
8709
+ callback = function( _, isAbort ) {
8710
+ var status, responseHeaders, statusText, responses;
8711
+
8712
+ // Firefox throws exceptions when accessing properties
8713
+ // of an xhr when a network error occurred
8714
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
8715
+ try {
8716
+
8717
+ // Was never called and is aborted or complete
8718
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
8719
+
8720
+ // Only called once
8721
+ callback = undefined;
8722
+
8723
+ // Do not keep as active anymore
8724
+ if ( handle ) {
8725
+ xhr.onreadystatechange = jQuery.noop;
8726
+ if ( xhrOnUnloadAbort ) {
8727
+ delete xhrCallbacks[ handle ];
8728
+ }
8729
+ }
8730
+
8731
+ // If it's an abort
8732
+ if ( isAbort ) {
8733
+ // Abort it manually if needed
8734
+ if ( xhr.readyState !== 4 ) {
8735
+ xhr.abort();
8736
+ }
8737
+ } else {
8738
+ responses = {};
8739
+ status = xhr.status;
8740
+ responseHeaders = xhr.getAllResponseHeaders();
8741
+
8742
+ // When requesting binary data, IE6-9 will throw an exception
8743
+ // on any attempt to access responseText (#11426)
8744
+ if ( typeof xhr.responseText === "string" ) {
8745
+ responses.text = xhr.responseText;
8746
+ }
8747
+
8748
+ // Firefox throws an exception when accessing
8749
+ // statusText for faulty cross-domain requests
8750
+ try {
8751
+ statusText = xhr.statusText;
8752
+ } catch( e ) {
8753
+ // We normalize with Webkit giving an empty statusText
8754
+ statusText = "";
8755
+ }
8756
+
8757
+ // Filter status for non standard behaviors
8758
+
8759
+ // If the request is local and we have data: assume a success
8760
+ // (success with no data won't get notified, that's the best we
8761
+ // can do given current implementations)
8762
+ if ( !status && s.isLocal && !s.crossDomain ) {
8763
+ status = responses.text ? 200 : 404;
8764
+ // IE - #1450: sometimes returns 1223 when it should be 204
8765
+ } else if ( status === 1223 ) {
8766
+ status = 204;
8767
+ }
8768
+ }
8769
+ }
8770
+ } catch( firefoxAccessException ) {
8771
+ if ( !isAbort ) {
8772
+ complete( -1, firefoxAccessException );
8773
+ }
8774
+ }
8775
+
8776
+ // Call complete if needed
8777
+ if ( responses ) {
8778
+ complete( status, statusText, responses, responseHeaders );
8779
+ }
8780
+ };
8781
+
8782
+ if ( !s.async ) {
8783
+ // if we're in sync mode we fire the callback
8784
+ callback();
8785
+ } else if ( xhr.readyState === 4 ) {
8786
+ // (IE6 & IE7) if it's in cache and has been
8787
+ // retrieved directly we need to fire the callback
8788
+ setTimeout( callback );
8789
+ } else {
8790
+ handle = ++xhrId;
8791
+ if ( xhrOnUnloadAbort ) {
8792
+ // Create the active xhrs callbacks list if needed
8793
+ // and attach the unload handler
8794
+ if ( !xhrCallbacks ) {
8795
+ xhrCallbacks = {};
8796
+ jQuery( window ).unload( xhrOnUnloadAbort );
8797
+ }
8798
+ // Add to list of active xhrs callbacks
8799
+ xhrCallbacks[ handle ] = callback;
8800
+ }
8801
+ xhr.onreadystatechange = callback;
8802
+ }
8803
+ },
8804
+
8805
+ abort: function() {
8806
+ if ( callback ) {
8807
+ callback( undefined, true );
8808
+ }
8809
+ }
8810
+ };
8811
+ }
8812
+ });
8813
+ }
8814
+ var fxNow, timerId,
8815
+ rfxtypes = /^(?:toggle|show|hide)$/,
8816
+ rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
8817
+ rrun = /queueHooks$/,
8818
+ animationPrefilters = [ defaultPrefilter ],
8819
+ tweeners = {
8820
+ "*": [function( prop, value ) {
8821
+ var tween = this.createTween( prop, value ),
8822
+ target = tween.cur(),
8823
+ parts = rfxnum.exec( value ),
8824
+ unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
8825
+
8826
+ // Starting value computation is required for potential unit mismatches
8827
+ start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
8828
+ rfxnum.exec( jQuery.css( tween.elem, prop ) ),
8829
+ scale = 1,
8830
+ maxIterations = 20;
8831
+
8832
+ if ( start && start[ 3 ] !== unit ) {
8833
+ // Trust units reported by jQuery.css
8834
+ unit = unit || start[ 3 ];
8835
+
8836
+ // Make sure we update the tween properties later on
8837
+ parts = parts || [];
8838
+
8839
+ // Iteratively approximate from a nonzero starting point
8840
+ start = +target || 1;
8841
+
8842
+ do {
8843
+ // If previous iteration zeroed out, double until we get *something*
8844
+ // Use a string for doubling factor so we don't accidentally see scale as unchanged below
8845
+ scale = scale || ".5";
8846
+
8847
+ // Adjust and apply
8848
+ start = start / scale;
8849
+ jQuery.style( tween.elem, prop, start + unit );
8850
+
8851
+ // Update scale, tolerating zero or NaN from tween.cur()
8852
+ // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
8853
+ } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
8854
+ }
8855
+
8856
+ // Update tween properties
8857
+ if ( parts ) {
8858
+ start = tween.start = +start || +target || 0;
8859
+ tween.unit = unit;
8860
+ // If a +=/-= token was provided, we're doing a relative animation
8861
+ tween.end = parts[ 1 ] ?
8862
+ start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
8863
+ +parts[ 2 ];
8864
+ }
8865
+
8866
+ return tween;
8867
+ }]
8868
+ };
8869
+
8870
+ // Animations created synchronously will run synchronously
8871
+ function createFxNow() {
8872
+ setTimeout(function() {
8873
+ fxNow = undefined;
8874
+ });
8875
+ return ( fxNow = jQuery.now() );
8876
+ }
8877
+
8878
+ function createTween( value, prop, animation ) {
8879
+ var tween,
8880
+ collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
8881
+ index = 0,
8882
+ length = collection.length;
8883
+ for ( ; index < length; index++ ) {
8884
+ if ( (tween = collection[ index ].call( animation, prop, value )) ) {
8885
+
8886
+ // we're done with this property
8887
+ return tween;
8888
+ }
8889
+ }
8890
+ }
8891
+
8892
+ function Animation( elem, properties, options ) {
8893
+ var result,
8894
+ stopped,
8895
+ index = 0,
8896
+ length = animationPrefilters.length,
8897
+ deferred = jQuery.Deferred().always( function() {
8898
+ // don't match elem in the :animated selector
8899
+ delete tick.elem;
8900
+ }),
8901
+ tick = function() {
8902
+ if ( stopped ) {
8903
+ return false;
8904
+ }
8905
+ var currentTime = fxNow || createFxNow(),
8906
+ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
8907
+ // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
8908
+ temp = remaining / animation.duration || 0,
8909
+ percent = 1 - temp,
8910
+ index = 0,
8911
+ length = animation.tweens.length;
8912
+
8913
+ for ( ; index < length ; index++ ) {
8914
+ animation.tweens[ index ].run( percent );
8915
+ }
8916
+
8917
+ deferred.notifyWith( elem, [ animation, percent, remaining ]);
8918
+
8919
+ if ( percent < 1 && length ) {
8920
+ return remaining;
8921
+ } else {
8922
+ deferred.resolveWith( elem, [ animation ] );
8923
+ return false;
8924
+ }
8925
+ },
8926
+ animation = deferred.promise({
8927
+ elem: elem,
8928
+ props: jQuery.extend( {}, properties ),
8929
+ opts: jQuery.extend( true, { specialEasing: {} }, options ),
8930
+ originalProperties: properties,
8931
+ originalOptions: options,
8932
+ startTime: fxNow || createFxNow(),
8933
+ duration: options.duration,
8934
+ tweens: [],
8935
+ createTween: function( prop, end ) {
8936
+ var tween = jQuery.Tween( elem, animation.opts, prop, end,
8937
+ animation.opts.specialEasing[ prop ] || animation.opts.easing );
8938
+ animation.tweens.push( tween );
8939
+ return tween;
8940
+ },
8941
+ stop: function( gotoEnd ) {
8942
+ var index = 0,
8943
+ // if we are going to the end, we want to run all the tweens
8944
+ // otherwise we skip this part
8945
+ length = gotoEnd ? animation.tweens.length : 0;
8946
+ if ( stopped ) {
8947
+ return this;
8948
+ }
8949
+ stopped = true;
8950
+ for ( ; index < length ; index++ ) {
8951
+ animation.tweens[ index ].run( 1 );
8952
+ }
8953
+
8954
+ // resolve when we played the last frame
8955
+ // otherwise, reject
8956
+ if ( gotoEnd ) {
8957
+ deferred.resolveWith( elem, [ animation, gotoEnd ] );
8958
+ } else {
8959
+ deferred.rejectWith( elem, [ animation, gotoEnd ] );
8960
+ }
8961
+ return this;
8962
+ }
8963
+ }),
8964
+ props = animation.props;
8965
+
8966
+ propFilter( props, animation.opts.specialEasing );
8967
+
8968
+ for ( ; index < length ; index++ ) {
8969
+ result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
8970
+ if ( result ) {
8971
+ return result;
8972
+ }
8973
+ }
8974
+
8975
+ jQuery.map( props, createTween, animation );
8976
+
8977
+ if ( jQuery.isFunction( animation.opts.start ) ) {
8978
+ animation.opts.start.call( elem, animation );
8979
+ }
8980
+
8981
+ jQuery.fx.timer(
8982
+ jQuery.extend( tick, {
8983
+ elem: elem,
8984
+ anim: animation,
8985
+ queue: animation.opts.queue
8986
+ })
8987
+ );
8988
+
8989
+ // attach callbacks from options
8990
+ return animation.progress( animation.opts.progress )
8991
+ .done( animation.opts.done, animation.opts.complete )
8992
+ .fail( animation.opts.fail )
8993
+ .always( animation.opts.always );
8994
+ }
8995
+
8996
+ function propFilter( props, specialEasing ) {
8997
+ var index, name, easing, value, hooks;
8998
+
8999
+ // camelCase, specialEasing and expand cssHook pass
9000
+ for ( index in props ) {
9001
+ name = jQuery.camelCase( index );
9002
+ easing = specialEasing[ name ];
9003
+ value = props[ index ];
9004
+ if ( jQuery.isArray( value ) ) {
9005
+ easing = value[ 1 ];
9006
+ value = props[ index ] = value[ 0 ];
9007
+ }
9008
+
9009
+ if ( index !== name ) {
9010
+ props[ name ] = value;
9011
+ delete props[ index ];
9012
+ }
9013
+
9014
+ hooks = jQuery.cssHooks[ name ];
9015
+ if ( hooks && "expand" in hooks ) {
9016
+ value = hooks.expand( value );
9017
+ delete props[ name ];
9018
+
9019
+ // not quite $.extend, this wont overwrite keys already present.
9020
+ // also - reusing 'index' from above because we have the correct "name"
9021
+ for ( index in value ) {
9022
+ if ( !( index in props ) ) {
9023
+ props[ index ] = value[ index ];
9024
+ specialEasing[ index ] = easing;
9025
+ }
9026
+ }
9027
+ } else {
9028
+ specialEasing[ name ] = easing;
9029
+ }
9030
+ }
9031
+ }
9032
+
9033
+ jQuery.Animation = jQuery.extend( Animation, {
9034
+
9035
+ tweener: function( props, callback ) {
9036
+ if ( jQuery.isFunction( props ) ) {
9037
+ callback = props;
9038
+ props = [ "*" ];
9039
+ } else {
9040
+ props = props.split(" ");
9041
+ }
9042
+
9043
+ var prop,
9044
+ index = 0,
9045
+ length = props.length;
9046
+
9047
+ for ( ; index < length ; index++ ) {
9048
+ prop = props[ index ];
9049
+ tweeners[ prop ] = tweeners[ prop ] || [];
9050
+ tweeners[ prop ].unshift( callback );
9051
+ }
9052
+ },
9053
+
9054
+ prefilter: function( callback, prepend ) {
9055
+ if ( prepend ) {
9056
+ animationPrefilters.unshift( callback );
9057
+ } else {
9058
+ animationPrefilters.push( callback );
9059
+ }
9060
+ }
9061
+ });
9062
+
9063
+ function defaultPrefilter( elem, props, opts ) {
9064
+ /* jshint validthis: true */
9065
+ var prop, value, toggle, tween, hooks, oldfire,
9066
+ anim = this,
9067
+ orig = {},
9068
+ style = elem.style,
9069
+ hidden = elem.nodeType && isHidden( elem ),
9070
+ dataShow = jQuery._data( elem, "fxshow" );
9071
+
9072
+ // handle queue: false promises
9073
+ if ( !opts.queue ) {
9074
+ hooks = jQuery._queueHooks( elem, "fx" );
9075
+ if ( hooks.unqueued == null ) {
9076
+ hooks.unqueued = 0;
9077
+ oldfire = hooks.empty.fire;
9078
+ hooks.empty.fire = function() {
9079
+ if ( !hooks.unqueued ) {
9080
+ oldfire();
9081
+ }
9082
+ };
9083
+ }
9084
+ hooks.unqueued++;
9085
+
9086
+ anim.always(function() {
9087
+ // doing this makes sure that the complete handler will be called
9088
+ // before this completes
9089
+ anim.always(function() {
9090
+ hooks.unqueued--;
9091
+ if ( !jQuery.queue( elem, "fx" ).length ) {
9092
+ hooks.empty.fire();
9093
+ }
9094
+ });
9095
+ });
9096
+ }
9097
+
9098
+ // height/width overflow pass
9099
+ if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
9100
+ // Make sure that nothing sneaks out
9101
+ // Record all 3 overflow attributes because IE does not
9102
+ // change the overflow attribute when overflowX and
9103
+ // overflowY are set to the same value
9104
+ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
9105
+
9106
+ // Set display property to inline-block for height/width
9107
+ // animations on inline elements that are having width/height animated
9108
+ if ( jQuery.css( elem, "display" ) === "inline" &&
9109
+ jQuery.css( elem, "float" ) === "none" ) {
9110
+
9111
+ // inline-level elements accept inline-block;
9112
+ // block-level elements need to be inline with layout
9113
+ if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
9114
+ style.display = "inline-block";
9115
+
9116
+ } else {
9117
+ style.zoom = 1;
9118
+ }
9119
+ }
9120
+ }
9121
+
9122
+ if ( opts.overflow ) {
9123
+ style.overflow = "hidden";
9124
+ if ( !jQuery.support.shrinkWrapBlocks ) {
9125
+ anim.always(function() {
9126
+ style.overflow = opts.overflow[ 0 ];
9127
+ style.overflowX = opts.overflow[ 1 ];
9128
+ style.overflowY = opts.overflow[ 2 ];
9129
+ });
9130
+ }
9131
+ }
9132
+
9133
+
9134
+ // show/hide pass
9135
+ for ( prop in props ) {
9136
+ value = props[ prop ];
9137
+ if ( rfxtypes.exec( value ) ) {
9138
+ delete props[ prop ];
9139
+ toggle = toggle || value === "toggle";
9140
+ if ( value === ( hidden ? "hide" : "show" ) ) {
9141
+ continue;
9142
+ }
9143
+ orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
9144
+ }
9145
+ }
9146
+
9147
+ if ( !jQuery.isEmptyObject( orig ) ) {
9148
+ if ( dataShow ) {
9149
+ if ( "hidden" in dataShow ) {
9150
+ hidden = dataShow.hidden;
9151
+ }
9152
+ } else {
9153
+ dataShow = jQuery._data( elem, "fxshow", {} );
9154
+ }
9155
+
9156
+ // store state if its toggle - enables .stop().toggle() to "reverse"
9157
+ if ( toggle ) {
9158
+ dataShow.hidden = !hidden;
9159
+ }
9160
+ if ( hidden ) {
9161
+ jQuery( elem ).show();
9162
+ } else {
9163
+ anim.done(function() {
9164
+ jQuery( elem ).hide();
9165
+ });
9166
+ }
9167
+ anim.done(function() {
9168
+ var prop;
9169
+ jQuery._removeData( elem, "fxshow" );
9170
+ for ( prop in orig ) {
9171
+ jQuery.style( elem, prop, orig[ prop ] );
9172
+ }
9173
+ });
9174
+ for ( prop in orig ) {
9175
+ tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
9176
+
9177
+ if ( !( prop in dataShow ) ) {
9178
+ dataShow[ prop ] = tween.start;
9179
+ if ( hidden ) {
9180
+ tween.end = tween.start;
9181
+ tween.start = prop === "width" || prop === "height" ? 1 : 0;
9182
+ }
9183
+ }
9184
+ }
9185
+ }
9186
+ }
9187
+
9188
+ function Tween( elem, options, prop, end, easing ) {
9189
+ return new Tween.prototype.init( elem, options, prop, end, easing );
9190
+ }
9191
+ jQuery.Tween = Tween;
9192
+
9193
+ Tween.prototype = {
9194
+ constructor: Tween,
9195
+ init: function( elem, options, prop, end, easing, unit ) {
9196
+ this.elem = elem;
9197
+ this.prop = prop;
9198
+ this.easing = easing || "swing";
9199
+ this.options = options;
9200
+ this.start = this.now = this.cur();
9201
+ this.end = end;
9202
+ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
9203
+ },
9204
+ cur: function() {
9205
+ var hooks = Tween.propHooks[ this.prop ];
9206
+
9207
+ return hooks && hooks.get ?
9208
+ hooks.get( this ) :
9209
+ Tween.propHooks._default.get( this );
9210
+ },
9211
+ run: function( percent ) {
9212
+ var eased,
9213
+ hooks = Tween.propHooks[ this.prop ];
9214
+
9215
+ if ( this.options.duration ) {
9216
+ this.pos = eased = jQuery.easing[ this.easing ](
9217
+ percent, this.options.duration * percent, 0, 1, this.options.duration
9218
+ );
9219
+ } else {
9220
+ this.pos = eased = percent;
9221
+ }
9222
+ this.now = ( this.end - this.start ) * eased + this.start;
9223
+
9224
+ if ( this.options.step ) {
9225
+ this.options.step.call( this.elem, this.now, this );
9226
+ }
9227
+
9228
+ if ( hooks && hooks.set ) {
9229
+ hooks.set( this );
9230
+ } else {
9231
+ Tween.propHooks._default.set( this );
9232
+ }
9233
+ return this;
9234
+ }
9235
+ };
9236
+
9237
+ Tween.prototype.init.prototype = Tween.prototype;
9238
+
9239
+ Tween.propHooks = {
9240
+ _default: {
9241
+ get: function( tween ) {
9242
+ var result;
9243
+
9244
+ if ( tween.elem[ tween.prop ] != null &&
9245
+ (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
9246
+ return tween.elem[ tween.prop ];
9247
+ }
9248
+
9249
+ // passing an empty string as a 3rd parameter to .css will automatically
9250
+ // attempt a parseFloat and fallback to a string if the parse fails
9251
+ // so, simple values such as "10px" are parsed to Float.
9252
+ // complex values such as "rotate(1rad)" are returned as is.
9253
+ result = jQuery.css( tween.elem, tween.prop, "" );
9254
+ // Empty strings, null, undefined and "auto" are converted to 0.
9255
+ return !result || result === "auto" ? 0 : result;
9256
+ },
9257
+ set: function( tween ) {
9258
+ // use step hook for back compat - use cssHook if its there - use .style if its
9259
+ // available and use plain properties where available
9260
+ if ( jQuery.fx.step[ tween.prop ] ) {
9261
+ jQuery.fx.step[ tween.prop ]( tween );
9262
+ } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
9263
+ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
9264
+ } else {
9265
+ tween.elem[ tween.prop ] = tween.now;
9266
+ }
9267
+ }
9268
+ }
9269
+ };
9270
+
9271
+ // Support: IE <=9
9272
+ // Panic based approach to setting things on disconnected nodes
9273
+
9274
+ Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
9275
+ set: function( tween ) {
9276
+ if ( tween.elem.nodeType && tween.elem.parentNode ) {
9277
+ tween.elem[ tween.prop ] = tween.now;
9278
+ }
9279
+ }
9280
+ };
9281
+
9282
+ jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
9283
+ var cssFn = jQuery.fn[ name ];
9284
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
9285
+ return speed == null || typeof speed === "boolean" ?
9286
+ cssFn.apply( this, arguments ) :
9287
+ this.animate( genFx( name, true ), speed, easing, callback );
9288
+ };
9289
+ });
9290
+
9291
+ jQuery.fn.extend({
9292
+ fadeTo: function( speed, to, easing, callback ) {
9293
+
9294
+ // show any hidden elements after setting opacity to 0
9295
+ return this.filter( isHidden ).css( "opacity", 0 ).show()
9296
+
9297
+ // animate to the value specified
9298
+ .end().animate({ opacity: to }, speed, easing, callback );
9299
+ },
9300
+ animate: function( prop, speed, easing, callback ) {
9301
+ var empty = jQuery.isEmptyObject( prop ),
9302
+ optall = jQuery.speed( speed, easing, callback ),
9303
+ doAnimation = function() {
9304
+ // Operate on a copy of prop so per-property easing won't be lost
9305
+ var anim = Animation( this, jQuery.extend( {}, prop ), optall );
9306
+
9307
+ // Empty animations, or finishing resolves immediately
9308
+ if ( empty || jQuery._data( this, "finish" ) ) {
9309
+ anim.stop( true );
9310
+ }
9311
+ };
9312
+ doAnimation.finish = doAnimation;
9313
+
9314
+ return empty || optall.queue === false ?
9315
+ this.each( doAnimation ) :
9316
+ this.queue( optall.queue, doAnimation );
9317
+ },
9318
+ stop: function( type, clearQueue, gotoEnd ) {
9319
+ var stopQueue = function( hooks ) {
9320
+ var stop = hooks.stop;
9321
+ delete hooks.stop;
9322
+ stop( gotoEnd );
9323
+ };
9324
+
9325
+ if ( typeof type !== "string" ) {
9326
+ gotoEnd = clearQueue;
9327
+ clearQueue = type;
9328
+ type = undefined;
9329
+ }
9330
+ if ( clearQueue && type !== false ) {
9331
+ this.queue( type || "fx", [] );
9332
+ }
9333
+
9334
+ return this.each(function() {
9335
+ var dequeue = true,
9336
+ index = type != null && type + "queueHooks",
9337
+ timers = jQuery.timers,
9338
+ data = jQuery._data( this );
9339
+
9340
+ if ( index ) {
9341
+ if ( data[ index ] && data[ index ].stop ) {
9342
+ stopQueue( data[ index ] );
9343
+ }
9344
+ } else {
9345
+ for ( index in data ) {
9346
+ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
9347
+ stopQueue( data[ index ] );
9348
+ }
9349
+ }
9350
+ }
9351
+
9352
+ for ( index = timers.length; index--; ) {
9353
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
9354
+ timers[ index ].anim.stop( gotoEnd );
9355
+ dequeue = false;
9356
+ timers.splice( index, 1 );
9357
+ }
9358
+ }
9359
+
9360
+ // start the next in the queue if the last step wasn't forced
9361
+ // timers currently will call their complete callbacks, which will dequeue
9362
+ // but only if they were gotoEnd
9363
+ if ( dequeue || !gotoEnd ) {
9364
+ jQuery.dequeue( this, type );
9365
+ }
9366
+ });
9367
+ },
9368
+ finish: function( type ) {
9369
+ if ( type !== false ) {
9370
+ type = type || "fx";
9371
+ }
9372
+ return this.each(function() {
9373
+ var index,
9374
+ data = jQuery._data( this ),
9375
+ queue = data[ type + "queue" ],
9376
+ hooks = data[ type + "queueHooks" ],
9377
+ timers = jQuery.timers,
9378
+ length = queue ? queue.length : 0;
9379
+
9380
+ // enable finishing flag on private data
9381
+ data.finish = true;
9382
+
9383
+ // empty the queue first
9384
+ jQuery.queue( this, type, [] );
9385
+
9386
+ if ( hooks && hooks.stop ) {
9387
+ hooks.stop.call( this, true );
9388
+ }
9389
+
9390
+ // look for any active animations, and finish them
9391
+ for ( index = timers.length; index--; ) {
9392
+ if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
9393
+ timers[ index ].anim.stop( true );
9394
+ timers.splice( index, 1 );
9395
+ }
9396
+ }
9397
+
9398
+ // look for any animations in the old queue and finish them
9399
+ for ( index = 0; index < length; index++ ) {
9400
+ if ( queue[ index ] && queue[ index ].finish ) {
9401
+ queue[ index ].finish.call( this );
9402
+ }
9403
+ }
9404
+
9405
+ // turn off finishing flag
9406
+ delete data.finish;
9407
+ });
9408
+ }
9409
+ });
9410
+
9411
+ // Generate parameters to create a standard animation
9412
+ function genFx( type, includeWidth ) {
9413
+ var which,
9414
+ attrs = { height: type },
9415
+ i = 0;
9416
+
9417
+ // if we include width, step value is 1 to do all cssExpand values,
9418
+ // if we don't include width, step value is 2 to skip over Left and Right
9419
+ includeWidth = includeWidth? 1 : 0;
9420
+ for( ; i < 4 ; i += 2 - includeWidth ) {
9421
+ which = cssExpand[ i ];
9422
+ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
9423
+ }
9424
+
9425
+ if ( includeWidth ) {
9426
+ attrs.opacity = attrs.width = type;
9427
+ }
9428
+
9429
+ return attrs;
9430
+ }
9431
+
9432
+ // Generate shortcuts for custom animations
9433
+ jQuery.each({
9434
+ slideDown: genFx("show"),
9435
+ slideUp: genFx("hide"),
9436
+ slideToggle: genFx("toggle"),
9437
+ fadeIn: { opacity: "show" },
9438
+ fadeOut: { opacity: "hide" },
9439
+ fadeToggle: { opacity: "toggle" }
9440
+ }, function( name, props ) {
9441
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
9442
+ return this.animate( props, speed, easing, callback );
9443
+ };
9444
+ });
9445
+
9446
+ jQuery.speed = function( speed, easing, fn ) {
9447
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
9448
+ complete: fn || !fn && easing ||
9449
+ jQuery.isFunction( speed ) && speed,
9450
+ duration: speed,
9451
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
9452
+ };
9453
+
9454
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
9455
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
9456
+
9457
+ // normalize opt.queue - true/undefined/null -> "fx"
9458
+ if ( opt.queue == null || opt.queue === true ) {
9459
+ opt.queue = "fx";
9460
+ }
9461
+
9462
+ // Queueing
9463
+ opt.old = opt.complete;
9464
+
9465
+ opt.complete = function() {
9466
+ if ( jQuery.isFunction( opt.old ) ) {
9467
+ opt.old.call( this );
9468
+ }
9469
+
9470
+ if ( opt.queue ) {
9471
+ jQuery.dequeue( this, opt.queue );
9472
+ }
9473
+ };
9474
+
9475
+ return opt;
9476
+ };
9477
+
9478
+ jQuery.easing = {
9479
+ linear: function( p ) {
9480
+ return p;
9481
+ },
9482
+ swing: function( p ) {
9483
+ return 0.5 - Math.cos( p*Math.PI ) / 2;
9484
+ }
9485
+ };
9486
+
9487
+ jQuery.timers = [];
9488
+ jQuery.fx = Tween.prototype.init;
9489
+ jQuery.fx.tick = function() {
9490
+ var timer,
9491
+ timers = jQuery.timers,
9492
+ i = 0;
9493
+
9494
+ fxNow = jQuery.now();
9495
+
9496
+ for ( ; i < timers.length; i++ ) {
9497
+ timer = timers[ i ];
9498
+ // Checks the timer has not already been removed
9499
+ if ( !timer() && timers[ i ] === timer ) {
9500
+ timers.splice( i--, 1 );
9501
+ }
9502
+ }
9503
+
9504
+ if ( !timers.length ) {
9505
+ jQuery.fx.stop();
9506
+ }
9507
+ fxNow = undefined;
9508
+ };
9509
+
9510
+ jQuery.fx.timer = function( timer ) {
9511
+ if ( timer() && jQuery.timers.push( timer ) ) {
9512
+ jQuery.fx.start();
9513
+ }
9514
+ };
9515
+
9516
+ jQuery.fx.interval = 13;
9517
+
9518
+ jQuery.fx.start = function() {
9519
+ if ( !timerId ) {
9520
+ timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
9521
+ }
9522
+ };
9523
+
9524
+ jQuery.fx.stop = function() {
9525
+ clearInterval( timerId );
9526
+ timerId = null;
9527
+ };
9528
+
9529
+ jQuery.fx.speeds = {
9530
+ slow: 600,
9531
+ fast: 200,
9532
+ // Default speed
9533
+ _default: 400
9534
+ };
9535
+
9536
+ // Back Compat <1.8 extension point
9537
+ jQuery.fx.step = {};
9538
+
9539
+ if ( jQuery.expr && jQuery.expr.filters ) {
9540
+ jQuery.expr.filters.animated = function( elem ) {
9541
+ return jQuery.grep(jQuery.timers, function( fn ) {
9542
+ return elem === fn.elem;
9543
+ }).length;
9544
+ };
9545
+ }
9546
+ jQuery.fn.offset = function( options ) {
9547
+ if ( arguments.length ) {
9548
+ return options === undefined ?
9549
+ this :
9550
+ this.each(function( i ) {
9551
+ jQuery.offset.setOffset( this, options, i );
9552
+ });
9553
+ }
9554
+
9555
+ var docElem, win,
9556
+ box = { top: 0, left: 0 },
9557
+ elem = this[ 0 ],
9558
+ doc = elem && elem.ownerDocument;
9559
+
9560
+ if ( !doc ) {
9561
+ return;
9562
+ }
9563
+
9564
+ docElem = doc.documentElement;
9565
+
9566
+ // Make sure it's not a disconnected DOM node
9567
+ if ( !jQuery.contains( docElem, elem ) ) {
9568
+ return box;
9569
+ }
9570
+
9571
+ // If we don't have gBCR, just use 0,0 rather than error
9572
+ // BlackBerry 5, iOS 3 (original iPhone)
9573
+ if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
9574
+ box = elem.getBoundingClientRect();
9575
+ }
9576
+ win = getWindow( doc );
9577
+ return {
9578
+ top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
9579
+ left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
9580
+ };
9581
+ };
9582
+
9583
+ jQuery.offset = {
9584
+
9585
+ setOffset: function( elem, options, i ) {
9586
+ var position = jQuery.css( elem, "position" );
9587
+
9588
+ // set position first, in-case top/left are set even on static elem
9589
+ if ( position === "static" ) {
9590
+ elem.style.position = "relative";
9591
+ }
9592
+
9593
+ var curElem = jQuery( elem ),
9594
+ curOffset = curElem.offset(),
9595
+ curCSSTop = jQuery.css( elem, "top" ),
9596
+ curCSSLeft = jQuery.css( elem, "left" ),
9597
+ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
9598
+ props = {}, curPosition = {}, curTop, curLeft;
9599
+
9600
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
9601
+ if ( calculatePosition ) {
9602
+ curPosition = curElem.position();
9603
+ curTop = curPosition.top;
9604
+ curLeft = curPosition.left;
9605
+ } else {
9606
+ curTop = parseFloat( curCSSTop ) || 0;
9607
+ curLeft = parseFloat( curCSSLeft ) || 0;
9608
+ }
9609
+
9610
+ if ( jQuery.isFunction( options ) ) {
9611
+ options = options.call( elem, i, curOffset );
9612
+ }
9613
+
9614
+ if ( options.top != null ) {
9615
+ props.top = ( options.top - curOffset.top ) + curTop;
9616
+ }
9617
+ if ( options.left != null ) {
9618
+ props.left = ( options.left - curOffset.left ) + curLeft;
9619
+ }
9620
+
9621
+ if ( "using" in options ) {
9622
+ options.using.call( elem, props );
9623
+ } else {
9624
+ curElem.css( props );
9625
+ }
9626
+ }
9627
+ };
9628
+
9629
+
9630
+ jQuery.fn.extend({
9631
+
9632
+ position: function() {
9633
+ if ( !this[ 0 ] ) {
9634
+ return;
9635
+ }
9636
+
9637
+ var offsetParent, offset,
9638
+ parentOffset = { top: 0, left: 0 },
9639
+ elem = this[ 0 ];
9640
+
9641
+ // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
9642
+ if ( jQuery.css( elem, "position" ) === "fixed" ) {
9643
+ // we assume that getBoundingClientRect is available when computed position is fixed
9644
+ offset = elem.getBoundingClientRect();
9645
+ } else {
9646
+ // Get *real* offsetParent
9647
+ offsetParent = this.offsetParent();
9648
+
9649
+ // Get correct offsets
9650
+ offset = this.offset();
9651
+ if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
9652
+ parentOffset = offsetParent.offset();
9653
+ }
9654
+
9655
+ // Add offsetParent borders
9656
+ parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
9657
+ parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
9658
+ }
9659
+
9660
+ // Subtract parent offsets and element margins
9661
+ // note: when an element has margin: auto the offsetLeft and marginLeft
9662
+ // are the same in Safari causing offset.left to incorrectly be 0
9663
+ return {
9664
+ top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
9665
+ left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
9666
+ };
9667
+ },
9668
+
9669
+ offsetParent: function() {
9670
+ return this.map(function() {
9671
+ var offsetParent = this.offsetParent || docElem;
9672
+ while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
9673
+ offsetParent = offsetParent.offsetParent;
9674
+ }
9675
+ return offsetParent || docElem;
9676
+ });
9677
+ }
9678
+ });
9679
+
9680
+
9681
+ // Create scrollLeft and scrollTop methods
9682
+ jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
9683
+ var top = /Y/.test( prop );
9684
+
9685
+ jQuery.fn[ method ] = function( val ) {
9686
+ return jQuery.access( this, function( elem, method, val ) {
9687
+ var win = getWindow( elem );
9688
+
9689
+ if ( val === undefined ) {
9690
+ return win ? (prop in win) ? win[ prop ] :
9691
+ win.document.documentElement[ method ] :
9692
+ elem[ method ];
9693
+ }
9694
+
9695
+ if ( win ) {
9696
+ win.scrollTo(
9697
+ !top ? val : jQuery( win ).scrollLeft(),
9698
+ top ? val : jQuery( win ).scrollTop()
9699
+ );
9700
+
9701
+ } else {
9702
+ elem[ method ] = val;
9703
+ }
9704
+ }, method, val, arguments.length, null );
9705
+ };
9706
+ });
9707
+
9708
+ function getWindow( elem ) {
9709
+ return jQuery.isWindow( elem ) ?
9710
+ elem :
9711
+ elem.nodeType === 9 ?
9712
+ elem.defaultView || elem.parentWindow :
9713
+ false;
9714
+ }
9715
+ // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
9716
+ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
9717
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
9718
+ // margin is only for outerHeight, outerWidth
9719
+ jQuery.fn[ funcName ] = function( margin, value ) {
9720
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
9721
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
9722
+
9723
+ return jQuery.access( this, function( elem, type, value ) {
9724
+ var doc;
9725
+
9726
+ if ( jQuery.isWindow( elem ) ) {
9727
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
9728
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
9729
+ // https://github.com/jquery/jquery/pull/764
9730
+ return elem.document.documentElement[ "client" + name ];
9731
+ }
9732
+
9733
+ // Get document width or height
9734
+ if ( elem.nodeType === 9 ) {
9735
+ doc = elem.documentElement;
9736
+
9737
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
9738
+ // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
9739
+ return Math.max(
9740
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
9741
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
9742
+ doc[ "client" + name ]
9743
+ );
9744
+ }
9745
+
9746
+ return value === undefined ?
9747
+ // Get width or height on the element, requesting but not forcing parseFloat
9748
+ jQuery.css( elem, type, extra ) :
9749
+
9750
+ // Set width or height on the element
9751
+ jQuery.style( elem, type, value, extra );
9752
+ }, type, chainable ? margin : undefined, chainable, null );
9753
+ };
9754
+ });
9755
+ });
9756
+ // Limit scope pollution from any deprecated API
9757
+ // (function() {
9758
+
9759
+ // The number of elements contained in the matched element set
9760
+ jQuery.fn.size = function() {
9761
+ return this.length;
9762
+ };
9763
+
9764
+ jQuery.fn.andSelf = jQuery.fn.addBack;
9765
+
9766
+ // })();
9767
+ if ( typeof module === "object" && module && typeof module.exports === "object" ) {
9768
+ // Expose jQuery as module.exports in loaders that implement the Node
9769
+ // module pattern (including browserify). Do not create the global, since
9770
+ // the user will be storing it themselves locally, and globals are frowned
9771
+ // upon in the Node module world.
9772
+ module.exports = jQuery;
9773
+ } else {
9774
+ // Otherwise expose jQuery to the global object as usual
9775
+ window.jQuery = window.$ = jQuery;
9776
+
9777
+ // Register as a named AMD module, since jQuery can be concatenated with other
9778
+ // files that may use define, but not via a proper concatenation script that
9779
+ // understands anonymous AMD modules. A named AMD is safest and most robust
9780
+ // way to register. Lowercase jquery is used because AMD module names are
9781
+ // derived from file names, and jQuery is normally delivered in a lowercase
9782
+ // file name. Do this after creating the global so that if an AMD module wants
9783
+ // to call noConflict to hide this version of jQuery, it will work.
9784
+ if ( typeof define === "function" && define.amd ) {
9785
+ define( "jquery", [], function () { return jQuery; } );
9786
+ }
9787
+ }
9788
+
9789
+ })( window );
js/pack/recaptchalib.php ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * This is a PHP library that handles calling reCAPTCHA.
4
+ * - Documentation and latest version
5
+ * http://recaptcha.net/plugins/php/
6
+ * - Get a reCAPTCHA API Key
7
+ * https://www.google.com/recaptcha/admin/create
8
+ * - Discussion group
9
+ * http://groups.google.com/group/recaptcha
10
+ *
11
+ * Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
12
+ * AUTHORS:
13
+ * Mike Crawford
14
+ * Ben Maurer
15
+ *
16
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
17
+ * of this software and associated documentation files (the "Software"), to deal
18
+ * in the Software without restriction, including without limitation the rights
19
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
+ * copies of the Software, and to permit persons to whom the Software is
21
+ * furnished to do so, subject to the following conditions:
22
+ *
23
+ * The above copyright notice and this permission notice shall be included in
24
+ * all copies or substantial portions of the Software.
25
+ *
26
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32
+ * THE SOFTWARE.
33
+ */
34
+
35
+ /**
36
+ * The reCAPTCHA server URL's
37
+ */
38
+ define("RECAPTCHA_API_SERVER", "http://www.google.com/recaptcha/api");
39
+ define("RECAPTCHA_API_SECURE_SERVER", "https://www.google.com/recaptcha/api");
40
+ define("RECAPTCHA_VERIFY_SERVER", "www.google.com");
41
+
42
+ /**
43
+ * Encodes the given data into a query string format
44
+ * @param $data - array of string elements to be encoded
45
+ * @return string - encoded request
46
+ */
47
+ function _recaptcha_qsencode ($data) {
48
+ $req = "";
49
+ foreach ( $data as $key => $value )
50
+ $req .= $key . '=' . urlencode( stripslashes($value) ) . '&';
51
+
52
+ // Cut the last '&'
53
+ $req=substr($req,0,strlen($req)-1);
54
+ return $req;
55
+ }
56
+
57
+
58
+
59
+ /**
60
+ * Submits an HTTP POST to a reCAPTCHA server
61
+ * @param string $host
62
+ * @param string $path
63
+ * @param array $data
64
+ * @param int port
65
+ * @return array response
66
+ */
67
+ function _recaptcha_http_post($host, $path, $data, $port = 80) {
68
+
69
+ $req = _recaptcha_qsencode ($data);
70
+
71
+ $http_request = "POST $path HTTP/1.0\r\n";
72
+ $http_request .= "Host: $host\r\n";
73
+ $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
74
+ $http_request .= "Content-Length: " . strlen($req) . "\r\n";
75
+ $http_request .= "User-Agent: reCAPTCHA/PHP\r\n";
76
+ $http_request .= "\r\n";
77
+ $http_request .= $req;
78
+
79
+ $response = '';
80
+ if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) {
81
+ die ('Could not open socket');
82
+ }
83
+
84
+ fwrite($fs, $http_request);
85
+
86
+ while ( !feof($fs) )
87
+ $response .= fgets($fs, 1160); // One TCP-IP packet
88
+ fclose($fs);
89
+ $response = explode("\r\n\r\n", $response, 2);
90
+
91
+ return $response;
92
+ }
93
+
94
+
95
+
96
+ /**
97
+ * Gets the challenge HTML (javascript and non-javascript version).
98
+ * This is called from the browser, and the resulting reCAPTCHA HTML widget
99
+ * is embedded within the HTML form it was called from.
100
+ * @param string $pubkey A public key for reCAPTCHA
101
+ * @param string $error The error given by reCAPTCHA (optional, default is null)
102
+ * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false)
103
+
104
+ * @return string - The HTML to be embedded in the user's form.
105
+ */
106
+ function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false)
107
+ {
108
+ if ($pubkey == null || $pubkey == '') {
109
+ die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
110
+ }
111
+
112
+ if ($use_ssl) {
113
+ $server = RECAPTCHA_API_SECURE_SERVER;
114
+ } else {
115
+ $server = RECAPTCHA_API_SERVER;
116
+ }
117
+
118
+ $errorpart = "";
119
+ if ($error) {
120
+ $errorpart = "&amp;error=" . $error;
121
+ }
122
+ return '<script type="text/javascript" src="'. $server . '/challenge?k=' . $pubkey . $errorpart . '"></script>
123
+
124
+ <noscript>
125
+ <iframe src="'. $server . '/noscript?k=' . $pubkey . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
126
+ <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
127
+ <input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
128
+ </noscript>';
129
+ }
130
+
131
+
132
+
133
+
134
+ /**
135
+ * A ReCaptchaResponse is returned from recaptcha_check_answer()
136
+ */
137
+ class ReCaptchaResponse {
138
+ var $is_valid;
139
+ var $error;
140
+ }
141
+
142
+
143
+ /**
144
+ * Calls an HTTP POST function to verify if the user's guess was correct
145
+ * @param string $privkey
146
+ * @param string $remoteip
147
+ * @param string $challenge
148
+ * @param string $response
149
+ * @param array $extra_params an array of extra variables to post to the server
150
+ * @return ReCaptchaResponse
151
+ */
152
+ function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array())
153
+ {
154
+ if ($privkey == null || $privkey == '') {
155
+ die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
156
+ }
157
+
158
+ if ($remoteip == null || $remoteip == '') {
159
+ die ("For security reasons, you must pass the remote ip to reCAPTCHA");
160
+ }
161
+
162
+
163
+
164
+ //discard spam submissions
165
+ if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) {
166
+ $recaptcha_response = new ReCaptchaResponse();
167
+ $recaptcha_response->is_valid = false;
168
+ $recaptcha_response->error = 'incorrect-captcha-sol';
169
+ return $recaptcha_response;
170
+ }
171
+
172
+ $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify",
173
+ array (
174
+ 'privatekey' => $privkey,
175
+ 'remoteip' => $remoteip,
176
+ 'challenge' => $challenge,
177
+ 'response' => $response
178
+ ) + $extra_params
179
+ );
180
+
181
+ $answers = explode ("\n", $response [1]);
182
+ $recaptcha_response = new ReCaptchaResponse();
183
+
184
+ if (trim ($answers [0]) == 'true') {
185
+ $recaptcha_response->is_valid = true;
186
+ }
187
+ else {
188
+ $recaptcha_response->is_valid = false;
189
+ $recaptcha_response->error = $answers [1];
190
+ }
191
+ return $recaptcha_response;
192
+
193
+ }
194
+
195
+ /**
196
+ * gets a URL where the user can sign up for reCAPTCHA. If your application
197
+ * has a configuration page where you enter a key, you should provide a link
198
+ * using this function.
199
+ * @param string $domain The domain where the page is hosted
200
+ * @param string $appname The name of your application
201
+ */
202
+ function recaptcha_get_signup_url ($domain = null, $appname = null) {
203
+ return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname));
204
+ }
205
+
206
+ function _recaptcha_aes_pad($val) {
207
+ $block_size = 16;
208
+ $numpad = $block_size - (strlen ($val) % $block_size);
209
+ return str_pad($val, strlen ($val) + $numpad, chr($numpad));
210
+ }
211
+
212
+ /* Mailhide related code */
213
+
214
+ function _recaptcha_aes_encrypt($val,$ky) {
215
+ if (! function_exists ("mcrypt_encrypt")) {
216
+ die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed.");
217
+ }
218
+ $mode=MCRYPT_MODE_CBC;
219
+ $enc=MCRYPT_RIJNDAEL_128;
220
+ $val=_recaptcha_aes_pad($val);
221
+ return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
222
+ }
223
+
224
+
225
+ function _recaptcha_mailhide_urlbase64 ($x) {
226
+ return strtr(base64_encode ($x), '+/', '-_');
227
+ }
228
+
229
+ /* gets the reCAPTCHA Mailhide url for a given email, public key and private key */
230
+ function recaptcha_mailhide_url($pubkey, $privkey, $email) {
231
+ if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) {
232
+ die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " .
233
+ "you can do so at <a href='http://www.google.com/recaptcha/mailhide/apikey'>http://www.google.com/recaptcha/mailhide/apikey</a>");
234
+ }
235
+
236
+
237
+ $ky = pack('H*', $privkey);
238
+ $cryptmail = _recaptcha_aes_encrypt ($email, $ky);
239
+
240
+ return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail);
241
+ }
242
+
243
+ /**
244
+ * gets the parts of the email to expose to the user.
245
+ * eg, given johndoe@example,com return ["john", "example.com"].
246
+ * the email is then displayed as john...@example.com
247
+ */
248
+ function _recaptcha_mailhide_email_parts ($email) {
249
+ $arr = preg_split("/@/", $email );
250
+
251
+ if (strlen ($arr[0]) <= 4) {
252
+ $arr[0] = substr ($arr[0], 0, 1);
253
+ } else if (strlen ($arr[0]) <= 6) {
254
+ $arr[0] = substr ($arr[0], 0, 3);
255
+ } else {
256
+ $arr[0] = substr ($arr[0], 0, 4);
257
+ }
258
+ return $arr;
259
+ }
260
+
261
+ /**
262
+ * Gets html to display an email address given a public an private key.
263
+ * to get a key, go to:
264
+ *
265
+ * http://www.google.com/recaptcha/mailhide/apikey
266
+ */
267
+ function recaptcha_mailhide_html($pubkey, $privkey, $email) {
268
+ $emailparts = _recaptcha_mailhide_email_parts ($email);
269
+ $url = recaptcha_mailhide_url ($pubkey, $privkey, $email);
270
+
271
+ return htmlentities($emailparts[0]) . "<a href='" . htmlentities ($url) .
272
+ "' onclick=\"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;\" title=\"Reveal this e-mail address\">...</a>@" . htmlentities ($emailparts [1]);
273
+
274
+ }
275
+
276
+
277
+ ?>
js/pack/support.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ echo '<div style=" float: none; margin: 130px 830px; position: absolute; ">
3
+ Support at :<br>
4
+ <p style="font-weight: bold;padding:5px">www.softechworld.com</p>
5
+ Email :
6
+ <p style="font-weight: bold;padding:5px">support@softechworld.com</p>
7
+
8
+ </div>';
9
+ ?>
10
+
js/pack/web.css ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #cont_tab tr td
2
+ {
3
+ text-align:right;
4
+ padding:7px;
5
+ }
6
+
7
+ #front tr td
8
+ {
9
+ padding:2px;
10
+ }
11
+
12
+ #front label
13
+ {
14
+ font-family:calibri;
15
+ font-size:15px;
16
+ }
17
+
18
+ #front input[type=text],input[type=tele],textarea
19
+ {
20
+ color: black;
21
+ font-family:cambria;
22
+
23
+ }
24
+
25
+ #front input[type=tele]
26
+ {
27
+ padding:5px;
28
+ }
package.xml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>ContactUs_Free</name>
4
+ <version>1.0.0.1</version>
5
+ <stability>stable</stability>
6
+ <license uri="www.softechworld.com">Softechworld</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>contact us extension</summary>
10
+ <description>This extension helps you to manage all fields whichever needed for contact us.&#xD;
11
+ You can directly set fields as compulsory or optional from admin section</description>
12
+ <notes>This is an stable version.</notes>
13
+ <authors><author><name>Softechworld</name><user>STW</user><email>info@softechworld.com</email></author></authors>
14
+ <date>2014-11-05</date>
15
+ <time>10:24:46</time>
16
+ <contents><target name="mage"><dir name="."><dir name="app"><dir name="code"><dir name="local"><dir name="Company"><dir name="Web"><dir name="Block"><dir name="Adminhtml"><dir name="Web"><dir name="Edit"><file name="Form.php" hash="db8db7ce94668d66e5f8adaa0153417d"/><dir name="Tab"><file name="Form.php" hash="78b26c9e7ecd6897496807d70d0c77b2"/><file name="Form1.php" hash="df44a4052a175910fa62c859db3a555f"/><file name="Form2.php" hash="f8304334e5659bcc21095de35d9be6a4"/><file name="Form3.php" hash="c38dece0dea164c36dc3f7cc6cf2f2c7"/></dir><file name="Tabs.php" hash="a084588fe11cc1fb24fce8775917dec4"/></dir><file name="Edit.php" hash="23d49a3af533e5f3bd779795fee6356a"/><file name="Grid.php" hash="559fd463d903e36ef847ebbb5d6978b2"/></dir><file name="Web.php" hash="b6256b48f49de3b3fc928dc0ebd7254d"/></dir><file name="Web.php" hash="ee4ee162f794296724ee4fe1ca76a5b0"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="WebController.php" hash="a5c2681b095f7634036e732c68910ca8"/></dir><file name="AjaxController.php" hash="2ac8ec02a890849f4d8278cdc5be9910"/><file name="IndexController.php" hash="ab7b236a977106daf8c1d88a8c17f641"/></dir><dir name="etc"><file name="adminhtml.xml" hash="66cadb6f0e9d8c71310669e391408456"/><file name="config.xml" hash="759f38528016509a1f0b24c90cfc9f96"/></dir><dir name="Helper"><file name="Data.php" hash="c53f7434ab511c59d3a23c973d55fd92"/></dir><dir name="Model"><dir name="Mysql4"><dir name="Web"><file name="Collection.php" hash="01fd20807bb00f483614f4d7d8ee4e0d"/></dir><file name="Web.php" hash="0dc6da898926c8227d64ac16ede3dafc"/></dir><file name="Status.php" hash="b23fbdcb4d49e95cf53184b57d66274f"/><file name="Web.php" hash="82eed865adf6b8c323f1624a34a9a466"/></dir><dir name="sql"><dir name="web_setup"><file name="mysql4-install-0.1.0.php" hash="dd98199c5f08fadb28227747659b0e6b"/></dir></dir></dir></dir></dir></dir><dir name="etc"><dir name="modules"><file name="Company_Web.xml" hash="102b0981f1e5b80ec434445eb015c8d6"/></dir></dir><dir name="design"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="web.xml" hash="892d1ea0b425e3d821fe9e00f34c70be"/></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="web.xml" hash="f95bb509ba7864a45ce75c3794be5958"/></dir><dir name="template"><dir name="web"><file name="web.phtml" hash="959cb608088acfeab7a67241b5f7a26f"/><file name="web1.phtml" hash="bd722328f115fd99acb316c31ca6671a"/></dir></dir></dir></dir></dir></dir></dir><dir name="js"><dir name="pack"><file name="PHPMailerAutoload.php" hash="afa2b6a11650a9ea43052e9a35429747"/><file name="class.phpmailer.php" hash="05858e3cfe2fcfa193fe1eafded0a407"/><file name="class.smtp.php" hash="cedca24273d6b541537662c6f5cde57f"/><file name="jquery-1.10.2.js" hash="7b89c0e044ada39b40994cccce1a3c3a"/><file name="recaptchalib.php" hash="b206569ed973563107c29902ca7ab35b"/><file name="support.php" hash="0ba5824f31770cfca64cd5dedd2d6598"/><file name="web.css" hash="78555af990f8b9fe8104fef63a982546"/></dir></dir></dir></target></contents>
17
+ <compatible/>
18
+ <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php></required></dependencies>
19
+ </package>