Wisepricer_Syncer - Version 1.1.2.5

Version Notes

wisepricer

Download this release

Release Info

Developer Moshe
Extension Wisepricer_Syncer
Version 1.1.2.5
Comparing to
See all releases


Version 1.1.2.5

Files changed (48) hide show
  1. app/code/local/Wisepricer/Syncer/Block/Adminhtml/Mapping.php +136 -0
  2. app/code/local/Wisepricer/Syncer/Helper/Data.php +11 -0
  3. app/code/local/Wisepricer/Syncer/Model/Adminhtml/Attributes.php +70 -0
  4. app/code/local/Wisepricer/Syncer/Model/Config.php +13 -0
  5. app/code/local/Wisepricer/Syncer/Model/Mapping.php +17 -0
  6. app/code/local/Wisepricer/Syncer/Model/Mysql4/Config.php +14 -0
  7. app/code/local/Wisepricer/Syncer/Model/Mysql4/Config/Collection.php +15 -0
  8. app/code/local/Wisepricer/Syncer/Model/Mysql4/Mapping.php +31 -0
  9. app/code/local/Wisepricer/Syncer/Model/Mysql4/Mapping/Collection.php +15 -0
  10. app/code/local/Wisepricer/Syncer/controllers/Adminhtml/SyncerController.php +182 -0
  11. app/code/local/Wisepricer/Syncer/controllers/ProductsController.php +430 -0
  12. app/code/local/Wisepricer/Syncer/etc/config.xml +126 -0
  13. app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/AES.php +479 -0
  14. app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/DES.php +945 -0
  15. app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/Hash.php +816 -0
  16. app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/RC4.php +493 -0
  17. app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/RSA.php +2119 -0
  18. app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/Random.php +129 -0
  19. app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/Rijndael.php +1242 -0
  20. app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/TripleDES.php +690 -0
  21. app/code/local/Wisepricer/Syncer/lib/phpseclib/Math/BigInteger.php +3545 -0
  22. app/code/local/Wisepricer/Syncer/lib/phpseclib/Net/SFTP.php +1461 -0
  23. app/code/local/Wisepricer/Syncer/lib/phpseclib/Net/SSH1.php +1159 -0
  24. app/code/local/Wisepricer/Syncer/lib/phpseclib/Net/SSH2.php +2302 -0
  25. app/code/local/Wisepricer/Syncer/lib/phpseclib/PHP/Compat/Function/array_fill.php +41 -0
  26. app/code/local/Wisepricer/Syncer/lib/phpseclib/PHP/Compat/Function/bcpowmod.php +66 -0
  27. app/code/local/Wisepricer/Syncer/lib/phpseclib/PHP/Compat/Function/str_split.php +59 -0
  28. app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-install-0.1.php +27 -0
  29. app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-install-1.0.0.php +27 -0
  30. app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-install-1.0.2.php +27 -0
  31. app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-upgrade-0.1-0.2.php +29 -0
  32. app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-upgrade-1.0.0-1.1.0.php +29 -0
  33. app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-upgrade-1.0.2-1.1.1.php +31 -0
  34. app/design/adminhtml/default/default/layout/syncer.xml +15 -0
  35. app/design/adminhtml/default/default/template/wisepricer/mapping.phtml +255 -0
  36. app/etc/modules/Wisepricer_Syncer.xml +23 -0
  37. package.xml +18 -0
  38. skin/adminhtml/default/default/images/wisepricer/bullet-green.png +0 -0
  39. skin/adminhtml/default/default/images/wisepricer/validation_advice_bg.gif +0 -0
  40. skin/adminhtml/default/default/images/wisepricer/wp-alert-icon.png +0 -0
  41. skin/adminhtml/default/default/images/wisepricer/wp-logo.png +0 -0
  42. skin/adminhtml/default/default/images/wisepricer/wp-save-btn.png +0 -0
  43. skin/adminhtml/default/default/wisepricer/chosen-sprite.png +0 -0
  44. skin/adminhtml/default/default/wisepricer/chosen.css +396 -0
  45. skin/adminhtml/default/default/wisepricer/chosen.proto.js +1007 -0
  46. skin/adminhtml/default/default/wisepricer/myprototype.js +6082 -0
  47. skin/adminhtml/default/default/wisepricer/prototype17.js +6082 -0
  48. skin/adminhtml/default/default/wisepricer/wisepricer.css +82 -0
app/code/local/Wisepricer/Syncer/Block/Adminhtml/Mapping.php ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /******************************************
4
+ * wisepricer *
5
+ ******************************************/
6
+
7
+ class Wisepricer_Syncer_Block_Adminhtml_Mapping extends Mage_Adminhtml_Block_Widget_Form_Container
8
+ {
9
+
10
+ public function getHeader()
11
+ {
12
+ $header = "Wisepricer Mapping";
13
+ return $header;
14
+ }
15
+
16
+ public function renderAttributesSelect($name,$id,$value=0,$class=''){
17
+ $attributesModel=Mage::getModel('wisepricer_syncer/adminhtml_attributes');
18
+ $options=$attributesModel->toOptionArray();
19
+ $mappingModel=Mage::getModel('wisepricer_syncer/mapping');
20
+ $selectHtml='<select style="width:209px" class="select '.$class.'" name="'.$name.'" id="'.$id.'">';
21
+
22
+ $row=$mappingModel->load($id,'wsp_field');
23
+ if($row->getData()){
24
+ $value=$row->getmagento_field();
25
+ }
26
+ foreach($options as $option){
27
+
28
+ if($option['value']==$value){
29
+ $selected='selected';
30
+ }else{
31
+ $selected='';
32
+ }
33
+
34
+ if($option['value']=='enable_googlecheckout'){
35
+ continue;
36
+ }
37
+
38
+ $selectHtml.='<option value="'.$option['value'].'" '.$selected.'>';
39
+ $selectHtml.=$option['label'];
40
+ $selectHtml.='</option>';
41
+ }
42
+ $selectHtml.='</select>';
43
+
44
+ return $selectHtml;
45
+ }
46
+
47
+ public function getShippingFixedRate(){
48
+
49
+ $shipping=$this->_getMagentoFieldByWsField('shipping');
50
+ if($shipping&&is_numeric($shipping)){
51
+ return $shipping;
52
+ }
53
+
54
+ return '';
55
+ }
56
+
57
+ public function getFixedMinPrice(){
58
+
59
+ $minprice=$this->_getMagentoFieldByWsField('minprice');
60
+ if($minprice&&is_numeric($minprice)){
61
+ return $minprice;
62
+ }
63
+
64
+ return '';
65
+ }
66
+
67
+ public function renderMinPriceRuleSelects(){
68
+
69
+ $model = Mage::getModel('wisepricer_syncer/mapping');
70
+ $mappingId=$model->loadIdByWsfield('minprice');
71
+ $valuesArr=array('','');
72
+
73
+ if($mappingId){
74
+ $minpriceRow=$model->load($mappingId);
75
+
76
+ $extra=$minpriceRow->getExtra();
77
+ if($extra){
78
+ $valuesArr=explode(':',$extra);
79
+ }else{
80
+ $valuesArr=array('1','a');
81
+ }
82
+
83
+
84
+ }else{
85
+ $valuesArr=array('1','a');
86
+ }
87
+
88
+ $cur=Mage::app()->getLocale()->currency(Mage::app()->getStore()->getCurrentCurrencyCode())->getSymbol();
89
+
90
+ $typeOptions=array(
91
+ array('label'=>'%','value'=>'-1'),
92
+ array('label'=>$cur,'value'=>'1')
93
+ );
94
+
95
+ $html=$this->_getCustomSelect('mapping_form[type]',$typeOptions,$valuesArr[0],'chzn-select function-select','width:50px');
96
+
97
+ $ruleOptions=array(
98
+ array('label'=>'Above cost','value'=>'a'),
99
+ array('label'=>'Below price','value'=>'b')
100
+ );
101
+
102
+ $html.=$this->_getCustomSelect('mapping_form[rule]',$ruleOptions,$valuesArr[1],'chzn-select function-select','width:136px');
103
+
104
+ return $html;
105
+ }
106
+
107
+ private function _getCustomSelect($name,$options,$default=0,$class='',$style='',$id=''){
108
+
109
+ $select='<select name="'.$name.'" id="'.$id.'" class="'.$class.'" style="'.$style.'">';
110
+ foreach ($options as $option) {
111
+ $selected='';
112
+ if($option['value']==$default){
113
+ $selected='selected';
114
+ }
115
+ $select.='<option value="'.$option['value'].'" '.$selected.'>'.$option['label'].'</option>';
116
+ }
117
+
118
+ $select.='</select>';
119
+
120
+ return $select;
121
+ }
122
+
123
+
124
+ private function _getMagentoFieldByWsField($wsfield){
125
+
126
+ $model = Mage::getModel('wisepricer_syncer/mapping');
127
+ $mappingId=$model->loadIdByWsfield($wsfield);
128
+ if($mappingId){
129
+ $mapping=$model->load($mappingId);
130
+ return $mapping->getmagento_field();
131
+ }
132
+
133
+ return false;
134
+ }
135
+ }
136
+ ?>
app/code/local/Wisepricer/Syncer/Helper/Data.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+
5
+ class Wisepricer_Syncer_Helper_Data extends Mage_Core_Helper_Abstract
6
+
7
+ {
8
+
9
+
10
+
11
+ }
app/code/local/Wisepricer/Syncer/Model/Adminhtml/Attributes.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Adminhtml
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Used in creating options for Yes|No config value selection
29
+ *
30
+ */
31
+ class Wisepricer_Syncer_Model_Adminhtml_Attributes
32
+ {
33
+ public $fields = array();
34
+ /**
35
+ * Options getter
36
+ *
37
+ * @return array
38
+ */
39
+ public function toOptionArray()
40
+ {
41
+ $this->fields=$this->getOptions();
42
+ return $this->fields;
43
+ }
44
+
45
+ public function getOptions()
46
+ {
47
+ $entityType = Mage::getModel('catalog/product')->getResource()->getEntityType();
48
+ $entityTypeId=$entityType->getId();
49
+ $attributeInfo = Mage::getResourceModel('eav/entity_attribute_collection')
50
+ ->setEntityTypeFilter($entityTypeId)
51
+ ->getData();
52
+ $result=array();
53
+ $result[]=array('value'=>'','label'=>'Choose an attribute');
54
+ foreach($attributeInfo as $_key=>$_value)
55
+ {
56
+ if($_value['is_global'] != "1" || $_value['is_visible']!="1"){
57
+ // continue;
58
+ }
59
+ if(isset($_value['frontend_label'])&&($_value['frontend_label']!='')){
60
+ $result[]=array('value'=>$_value['attribute_code'],'label' => $_value['frontend_label']);
61
+ }else{
62
+ $result[]=array('value'=>$_value['attribute_code'],'label' => $_value['attribute_code']);
63
+ }
64
+
65
+ }
66
+ $result[]=array('value'=>'qty','label'=>'Qty');
67
+ return $result;
68
+ }
69
+
70
+ }
app/code/local/Wisepricer/Syncer/Model/Config.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Wisepricer_Syncer_Model_Config extends Mage_Core_Model_Abstract
4
+ {
5
+
6
+ public function _construct()
7
+ {
8
+ parent::_construct();
9
+ $this->_init('wisepricer_syncer/config');
10
+ }
11
+ }
12
+
13
+ ?>
app/code/local/Wisepricer/Syncer/Model/Mapping.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Wisepricer_Syncer_Model_Mapping extends Mage_Core_Model_Abstract
4
+ {
5
+
6
+ public function _construct()
7
+ {
8
+ parent::_construct();
9
+ $this->_init('wisepricer_syncer/mapping');
10
+ }
11
+
12
+ public function loadIdByWsfield($ws_field){
13
+ return $this->_getResource()->loadIdByWsfield($this, $ws_field);
14
+ }
15
+ }
16
+
17
+ ?>
app/code/local/Wisepricer/Syncer/Model/Mysql4/Config.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Wisepricer_Syncer_Model_Mysql4_Config extends Mage_Core_Model_Mysql4_Abstract
4
+ {
5
+ /**
6
+ * Initialize resource
7
+ *
8
+ */
9
+ protected function _construct()
10
+ {
11
+ $this->_init('wisepricer_syncer/wisepricer_syncer_config', 'licensekey_id');
12
+ }
13
+ }
14
+ ?>
app/code/local/Wisepricer/Syncer/Model/Mysql4/Config/Collection.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Wisepricer_Syncer_Model_Mysql4_Config_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ /**
6
+ * Initialize collection
7
+ *
8
+ */
9
+ public function _construct()
10
+ {
11
+ $this->_init('wisepricer_syncer/wisepricer_syncer_config');
12
+ }
13
+ }
14
+
15
+ ?>
app/code/local/Wisepricer/Syncer/Model/Mysql4/Mapping.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Wisepricer_Syncer_Model_Mysql4_Mapping extends Mage_Core_Model_Mysql4_Abstract
4
+ {
5
+ /**
6
+ * Initialize resource
7
+ *
8
+ */
9
+ protected function _construct()
10
+ {
11
+ $this->_init('wisepricer_syncer/wisepricer_syncer_mapping', 'mapping_id');
12
+ }
13
+
14
+ public function loadIdByWsfield(Wisepricer_Syncer_Model_Mapping $mapping, $wsfield, $testOnly = false)
15
+ {
16
+ $adapter = $this->_getReadAdapter();
17
+ $bind = array('wsp_field' => $wsfield);
18
+ $select = $adapter->select()
19
+ ->from('wisepricer_syncer_mapping')
20
+ ->where('wsp_field = :wsp_field');
21
+
22
+
23
+
24
+ $mappingId = $adapter->fetchOne($select, $bind);
25
+
26
+ return $mappingId;
27
+
28
+
29
+ }
30
+ }
31
+ ?>
app/code/local/Wisepricer/Syncer/Model/Mysql4/Mapping/Collection.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Wisepricer_Syncer_Model_Mysql4_Mapping_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ /**
6
+ * Initialize collection
7
+ *
8
+ */
9
+ public function _construct()
10
+ {
11
+ $this->_init('wisepricer_syncer/wisepricer_syncer_mapping');
12
+ }
13
+ }
14
+
15
+ ?>
app/code/local/Wisepricer/Syncer/controllers/Adminhtml/SyncerController.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Performs integration between Wisepricer and Magento
4
+ *
5
+ */
6
+ class Wisepricer_Syncer_Adminhtml_SyncerController extends Mage_Adminhtml_Controller_Action
7
+ {
8
+ public function registrateAction()
9
+ {
10
+ $model = Mage::getModel('wisepricer_syncer/config');
11
+ $lisenceData=$model->load(1);
12
+ if(count($lisenceData->getData())>0){
13
+ if($lisenceData->getis_confirmed()==0){
14
+ Mage::getSingleton('adminhtml/session')->addError('Integration not complete.');
15
+ }
16
+ }else{
17
+ Mage::getSingleton('adminhtml/session')->addError('Integration not complete.');
18
+ }
19
+
20
+ $this->loadLayout()->_setActiveMenu('wisepricer');
21
+ $this->renderLayout();
22
+ }
23
+ public function mappingAction()
24
+ {
25
+ $model = Mage::getModel('wisepricer_syncer/config');
26
+ $lisenceData=$model->load(1);
27
+ if(count($lisenceData->getData())>0){
28
+ if($lisenceData->getis_confirmed()==0){
29
+ Mage::getSingleton('adminhtml/session')->addError('Integration not complete.');
30
+ }
31
+ }else{
32
+ Mage::getSingleton('adminhtml/session')->addError('Integration not complete.');
33
+ }
34
+
35
+ $this->loadLayout()->_setActiveMenu('wisepricer');
36
+ $this->renderLayout();
37
+ }
38
+ public function _savekey()
39
+ {
40
+ $post = $this->getRequest()->getPost('register_form');
41
+ $lisensekey = $post['licensekey'];
42
+ try {
43
+ if (empty($lisensekey)) {
44
+ Mage::throwException($this->__('Invalid form data. The license key is missing!'));
45
+ }
46
+ $model = Mage::getModel('wisepricer_syncer/config');
47
+ $lisenceData=$model->load(1);
48
+ if(count($lisenceData->getData())>0){
49
+ $lisenceData->setLicensekey($lisensekey);
50
+ $lisenceData->save();
51
+ }else{
52
+ $model->setLicensekey($lisensekey)->save();
53
+ }
54
+
55
+ }
56
+ catch (Exception $e) {
57
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
58
+ }
59
+ }
60
+ protected function _validateMapping($post){
61
+
62
+ $isValid=false;
63
+ if(
64
+ $post['upc']||
65
+ $post['asin']||
66
+ ($post['brand']&&$post['model'])||
67
+ ($post['brand']&&$post['mpn'])
68
+ ){
69
+ $isValid=true;
70
+ }
71
+ if($post['sku']==''||$post['title']==''||$post['price']==''){
72
+ $isValid=false;
73
+ }
74
+
75
+ if(($post['cost']==''||$post['minprice_man']=='')&&$post['minprice']==''){
76
+ $isValid=false;
77
+ }
78
+
79
+ return $isValid;
80
+ }
81
+ public function savemappingAction(){
82
+ $post= $this->getRequest()->getPost('mapping_form');
83
+ $this->_savekey();
84
+
85
+ $isValid=$this->_validateMapping($post);
86
+ if(!$post||!$isValid){
87
+ Mage::getSingleton('adminhtml/session')->addError('Your mapping is not valid.Please fill the data according to the instruction below.');
88
+ $this->_redirect('*/*/mapping');
89
+ return;
90
+ }
91
+ $data=array();
92
+ $model = Mage::getModel('wisepricer_syncer/mapping');
93
+ $allEntered=true;
94
+
95
+ foreach($post as $name=>$value){
96
+
97
+ if(!$value||$name=='type'||$name=='rule'){
98
+ continue;
99
+ }
100
+
101
+ $fieldName=$name;
102
+
103
+ if($name=='shipping_man'){
104
+ $fieldName='shipping';
105
+ }
106
+
107
+
108
+ $data=array('wsp_field'=>$fieldName,'magento_field'=>$value);
109
+
110
+ if($name=='minprice_man'){
111
+ $fieldName='minprice';
112
+ $function=$post['type'].':'.$post['rule'];
113
+ $data=array('wsp_field'=>$fieldName,'magento_field'=>$value,'extra'=>$function);
114
+ }
115
+
116
+ $mappingId=$model->loadIdByWsfield($fieldName);
117
+
118
+ if($mappingId){
119
+
120
+ $mapping=$model->load($mappingId);
121
+ $mapping->setmagento_field($value);
122
+
123
+ if($name=='minprice_man'){
124
+ $mapping->setExtra($function);
125
+ }
126
+
127
+ try {
128
+ $mapping->save()->getId();
129
+ } catch (Exception $e){
130
+ Mage::getSingleton('adminhtml/session')->addError('Insert of the field "'.$fieldName.'" failed with a following message: '.$e->getMessage());
131
+ $allEntered=false;
132
+ }
133
+
134
+ }else{
135
+ $model->setData($data);
136
+ try {
137
+ $model->save()->getId();
138
+ } catch (Exception $e){
139
+ Mage::getSingleton('adminhtml/session')->addError('Insert of the field "'.$fieldName.'" failed with a following message: '.$e->getMessage());
140
+ $allEntered=false;
141
+ }
142
+ }
143
+
144
+
145
+
146
+ }
147
+
148
+ $this->_redirect('*/*/mapping');
149
+ if($allEntered){
150
+ $lisenceModel=Mage::getModel('wisepricer_syncer/config')->load(1);
151
+ if($lisenceModel->getData()){
152
+ if($lisenceModel->getis_confirmed()==0){
153
+ $this->_createKeys($lisenceModel);
154
+ $lisenceModel->setis_confirmed(1)->save();
155
+ Mage::getSingleton('adminhtml/session')->addSuccess('Your integration with Wisepricer now is complete!');
156
+ }
157
+ }else{
158
+ Mage::throwException($this->__('Invalid form data. The license key is missing!'));
159
+ }
160
+ }
161
+
162
+ }
163
+
164
+ public function checkcompatAction(){
165
+
166
+ }
167
+
168
+ protected function _createKeys($lisenceModel){
169
+
170
+ set_include_path(get_include_path().PS .BP.DS . 'lib'.DS.'phpseclib' . PS.BP.DS.'app'.DS.'code'.DS.'local'.DS.'Wisepricer'.DS.'Syncer'.DS.'lib'.DS.'phpseclib' );
171
+ include('Crypt'.DS.'RSA.php');
172
+
173
+ $rsa = new Crypt_RSA();
174
+
175
+ // Create the keypair
176
+ $keyArr = $rsa->createKey();
177
+ $lisenceModel->setpublickey($keyArr['publickey']);
178
+ $lisenceModel->setprivatekey($keyArr['privatekey']);
179
+ }
180
+
181
+ }
182
+ ?>
app/code/local/Wisepricer/Syncer/controllers/ProductsController.php ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Performs integration between Wisepricer and Magento
4
+ *
5
+ */
6
+ class Wisepricer_Syncer_ProductsController extends Mage_Core_Controller_Front_Action
7
+ {
8
+
9
+ var $productOrigData=array();
10
+
11
+ public function sendAction(){
12
+ // Mage::log('start sending',null,'wplog.log');
13
+ set_time_limit (1800);
14
+ $licenseData =Mage::getModel('wisepricer_syncer/config')->load(1);
15
+ if(!$licenseData->getData()||$licenseData->getIs_confirmed()==0){
16
+
17
+ $returnArr=array(
18
+ 'status'=>'failure',
19
+ 'error_code'=>'769',
20
+ 'error_details'=>'The user has not completed the integration.'
21
+ );
22
+ echo json_encode($returnArr);
23
+ die;
24
+ }
25
+
26
+ $post = $this->getRequest()->getParams();
27
+
28
+ $magentoSessionId=Mage::getModel('core/cookie')->get('wpsession');
29
+
30
+ if($magentoSessionId!=$post['sesssionid']){
31
+
32
+ $returnArr=array(
33
+ 'status'=>'failure',
34
+ 'error_code'=>'771',
35
+ 'error_details'=>'Unauthorized access.'
36
+ );
37
+ echo json_encode($returnArr);
38
+ die;
39
+ }
40
+
41
+ //decryption process
42
+
43
+ $startInd = $post['start'];
44
+ if(!$startInd){
45
+ $startInd=0;
46
+ }
47
+
48
+ $count = $post['count'];
49
+ if(!$count||$count>200){
50
+ $count=200;
51
+ }
52
+ $fieldsEncoded= $post['params'];
53
+ $fields =json_decode(urldecode ($fieldsEncoded));
54
+
55
+ $mappings =Mage::getModel('wisepricer_syncer/mapping')->getCollection()->getData();
56
+
57
+ $collection=Mage::getModel('catalog/product')->getCollection();
58
+
59
+ $requiredFields=array();
60
+ foreach ($mappings as $fieldsArr) {
61
+ if($fields[0]=='all'||in_array($fieldsArr['wsp_field'],$fields)){
62
+ $requiredFields[]=$fieldsArr;
63
+ }
64
+
65
+ if(!is_numeric($fieldsArr['magento_field'])){
66
+ $collection->addAttributeToSelect($fieldsArr['magento_field']);
67
+ }
68
+
69
+ }
70
+
71
+
72
+ $productsOutput=array();
73
+
74
+
75
+
76
+ foreach ($collection as $product) {
77
+ // echo '<pre>'.print_r($product->getData(),true).'</pre>';die;
78
+ $productCollData=$product->getData();
79
+ $productModel=Mage::getModel('catalog/product')->load($productCollData['entity_id']);
80
+
81
+ //TODO add user control here - dont bring not saleble products
82
+ if(!$productModel->isSaleable()){
83
+ continue;
84
+ }
85
+ $this->productOrigData=$productModel->getData();
86
+ $productData=array();
87
+
88
+ foreach ($requiredFields as $field) {
89
+ if($field['wsp_field']=='stock'){
90
+
91
+ $qtyStock = Mage::getModel('cataloginventory/stock_item')->loadByProduct($product)->getQty();
92
+ $productData['stock']=(int)$qtyStock;
93
+
94
+ }elseif($field['wsp_field']=='productimage'){
95
+
96
+ if($field['magento_field']=='image'||$field['magento_field']=='small_image'){
97
+ $siteUrl=substr_replace(Mage::getUrl('media/catalog/product') ,"",-1);
98
+ $productData['productimage']=$siteUrl.$this->productOrigData['image'];
99
+ }else{
100
+ $productData['productimage']=$this->productOrigData['image'];
101
+ }
102
+
103
+
104
+ }elseif($field['wsp_field']=='shipping'){
105
+
106
+ if(is_numeric($productOrigData['shipping'])){
107
+ $productData['shipping']=$field['magento_field'];
108
+ }else{
109
+ $productData['shipping']=$this->productOrigData[$field['magento_field']];
110
+ }
111
+
112
+ }elseif($field['wsp_field']=='minprice'){
113
+ if(is_numeric($field['magento_field'])){
114
+
115
+ $productData[$field['wsp_field']]=$this->_calculateMinPrice($field);
116
+
117
+ }else{
118
+ $productData[$field['wsp_field']]=$this->productOrigData[$field['magento_field']];
119
+ }
120
+ }else{
121
+ $attributeInfo = Mage::getResourceModel('eav/entity_attribute_collection')
122
+ ->setCodeFilter($field['magento_field'])
123
+ ->getFirstItem();
124
+
125
+
126
+ if($attributeInfo->getfrontend_input()=='select'){
127
+ $attrLabel=$productModel->getAttributeText($field['magento_field']);
128
+ $productData[$field['wsp_field']]=$attrLabel;
129
+ // echo '<pre>'.print_r($attr,true).'</pre>';//die;
130
+ }else{
131
+ $productData[$field['wsp_field']]=$this->productOrigData[$field['magento_field']];
132
+ }
133
+
134
+
135
+ }
136
+
137
+
138
+ }
139
+
140
+ $productData['producturl']=Mage::helper('catalog/product')->getProductUrl($productCollData['entity_id']);
141
+
142
+ $productsOutput[]=$productData;
143
+ }
144
+ $productsOutput= array_splice($productsOutput, $startInd, $count);
145
+ // echo '<pre>'.print_r($productsOutput,true).'</pre>'; die;
146
+ $returnArr=array(
147
+ 'status'=>'success',
148
+ 'error_code'=>'0',
149
+ 'data'=>$productsOutput
150
+ );
151
+ // Mage::log('sending '.count($productsOutput).' products',null,'wplog.log');
152
+ echo json_encode($returnArr);
153
+
154
+ }
155
+
156
+ private function _calculateMinPrice($field){
157
+
158
+ $ruleArr= explode(':',$field['extra']);
159
+
160
+ if($ruleArr[1]=='a'){
161
+ $costField=$this->_getMagentoFieldByWsField('cost');
162
+ if($costField){
163
+ $cost=$this->productOrigData[$costField];
164
+ if($ruleArr[0]=='-1'){
165
+ $minPrice=($cost*$field['magento_field'])/100+$cost;
166
+ }else{
167
+ $minPrice=$cost+$field['magento_field'];
168
+ }
169
+ }
170
+ }else{
171
+ $priceField=$this->_getMagentoFieldByWsField('price');
172
+ $price=$this->productOrigData[$priceField];
173
+ if($ruleArr[0]=='-1'){
174
+ $minPrice=$price-($price*$field['magento_field'])/100;
175
+ }else{
176
+ $minPrice=$price-$field['magento_field'];
177
+ }
178
+ }
179
+ return $minPrice;
180
+
181
+ }
182
+
183
+ private function _getMagentoFieldByWsField($wsfield){
184
+
185
+ $model = Mage::getModel('wisepricer_syncer/mapping');
186
+ $mappingId=$model->loadIdByWsfield($wsfield);
187
+ if($mappingId){
188
+ $mapping=$model->load($mappingId);
189
+ return $mapping->getmagento_field();
190
+ }
191
+
192
+ return false;
193
+ }
194
+
195
+ private function _randString( $length ) {
196
+ $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
197
+ $str='';
198
+ $size = strlen( $chars );
199
+ for( $i = 0; $i < $length; $i++ ) {
200
+ $str .= $chars[ rand( 0, $size - 1 ) ];
201
+ }
202
+
203
+ return $str;
204
+ }
205
+
206
+ public function loginAction(){
207
+
208
+ $licenseData =Mage::getModel('wisepricer_syncer/config')->load(1);
209
+ if(!$licenseData->getData()||$licenseData->getIs_confirmed()==0){
210
+
211
+ $returnArr=array(
212
+ 'status'=>'failure',
213
+ 'error_code'=>'769',
214
+ 'error_details'=>'The user has not completed the integration.'
215
+ );
216
+ echo json_encode($returnArr);
217
+ die;
218
+ }
219
+
220
+ $post = $this->getRequest()->getParams();
221
+
222
+ $lisensekeyEncr = $post['licensekey'];
223
+ Mage::log(print_r($lisensekeyEncr,true),null,'wplog.log');
224
+ $lisensekeyEncr=pack('H*', $lisensekeyEncr);
225
+ Mage::log(print_r($lisensekeyEncr,true),null,'wplog.log');
226
+ $lisensekey=$this->_decryptstring($lisensekeyEncr);
227
+ Mage::log(print_r($lisensekey,true),null,'wplog.log');
228
+ if($licenseData->getLicensekey()!=$lisensekey){
229
+
230
+ $returnArr=array(
231
+ 'status'=>'failure',
232
+ 'error_code'=>'771',
233
+ 'error_details'=>'Unauthorized access.'
234
+ );
235
+ echo json_encode($returnArr);
236
+ die;
237
+ }
238
+
239
+ $sessionId=$this->_randString(8);
240
+
241
+ Mage::getModel('core/cookie')->set('wpsession',$sessionId , true);
242
+
243
+ $returnArr=array(
244
+ 'status'=>'success',
245
+ 'error_code'=>'0',
246
+ 'session_id'=>$sessionId
247
+ );
248
+ Mage::log(print_r('=========================================================',true),null,'wplog.log');
249
+ echo json_encode($returnArr);
250
+ }
251
+
252
+ public function logoutAction(){
253
+
254
+ $post = $this->getRequest()->getParams();
255
+
256
+ $magentoSessionId=Mage::getModel('core/cookie')->get('wpsession');
257
+
258
+ if($magentoSessionId!=$post['sesssionid']){
259
+
260
+ $returnArr=array(
261
+ 'status'=>'failure',
262
+ 'error_code'=>'771',
263
+ 'error_details'=>'Unauthorized access.'
264
+ );
265
+ echo json_encode($returnArr);
266
+ die;
267
+ }
268
+
269
+ Mage::getModel('core/cookie')->delete('wpsession');
270
+ $returnArr=array(
271
+ 'status'=>'success',
272
+ 'error_code'=>'0'
273
+ );
274
+
275
+ echo json_encode($returnArr);
276
+ }
277
+ public function getpublicAction(){
278
+
279
+ $publickey=$this->_getpublickey();
280
+
281
+ $returnArr=array(
282
+ 'status'=>'success',
283
+ 'error_code'=>'0',
284
+ 'publickey'=>$publickey
285
+ );
286
+
287
+ echo json_encode($returnArr);
288
+ }
289
+
290
+ protected function _getprivatekey(){
291
+
292
+ $licenseData =Mage::getModel('wisepricer_syncer/config')->load(1);
293
+ if(!$licenseData->getData()||$licenseData->getIs_confirmed()==0){
294
+
295
+ $returnArr=array(
296
+ 'status'=>'failure',
297
+ 'error_code'=>'769',
298
+ 'error_details'=>'The user has not completed the integration.'
299
+ );
300
+ echo json_encode($returnArr);
301
+ die;
302
+ }
303
+
304
+ return $licenseData->getprivatekey();
305
+ }
306
+
307
+ protected function _getpublickey(){
308
+
309
+ $licenseData =Mage::getModel('wisepricer_syncer/config')->load(1);
310
+ if(!$licenseData->getData()||$licenseData->getIs_confirmed()==0){
311
+
312
+ $returnArr=array(
313
+ 'status'=>'failure',
314
+ 'error_code'=>'769',
315
+ 'error_details'=>'The user has not completed the integration.'
316
+ );
317
+ echo json_encode($returnArr);
318
+ die;
319
+ }
320
+
321
+ return $licenseData->getpublickey();
322
+ }
323
+
324
+ protected function _decryptstring($str){
325
+
326
+ set_include_path(get_include_path().PS .BP.DS . 'lib'.DS.'phpseclib' . PS.BP.DS.'app'.DS.'code'.DS.'local'.DS.'Wisepricer'.DS.'Syncer'.DS.'lib'.DS.'phpseclib' );
327
+ include('Crypt'.DS.'RSA.php');
328
+
329
+ $rsa = new Crypt_RSA();
330
+ $rsa->loadKey($this->_getprivatekey());
331
+ return $rsa->decrypt($str);
332
+ }
333
+
334
+ public function repriceAction(){
335
+
336
+ $licenseData =Mage::getModel('wisepricer_syncer/config')->load(1);
337
+ if(!$licenseData->getData()||$licenseData->getIs_confirmed()==0){
338
+
339
+ $returnArr=array(
340
+ 'status'=>'failure',
341
+ 'error_code'=>'769',
342
+ 'error_details'=>'The user has not completed the integration.'
343
+ );
344
+ echo json_encode($returnArr);
345
+ die;
346
+ }
347
+
348
+ $post = $this->getRequest()->getParams();
349
+
350
+ $lisensekeyEncr = $post['licensekey'];
351
+
352
+ $lisensekey=$this->_decryptstring($lisensekeyEncr);
353
+
354
+ if($licenseData->getLicensekey()!=$lisensekey){
355
+
356
+ $returnArr=array(
357
+ 'status'=>'failure',
358
+ 'error_code'=>'771',
359
+ 'error_details'=>'Unauthorized access.'
360
+ );
361
+ echo json_encode($returnArr);
362
+ die;
363
+ }
364
+
365
+ $productsEncoded= $post['products'];
366
+ $products =json_decode(urldecode ($productsEncoded));
367
+ $responseArr=array();
368
+ $sucessCounter=0;
369
+ $failedCounter=0;
370
+ foreach ($products as $prodArr) {
371
+ $model = Mage::getModel('catalog/product');
372
+ $product = $model->load($model->getIdBySku($prodArr->sku));
373
+
374
+ try {
375
+ if ($product != null) {
376
+ if (isset($prodArr->price)) {
377
+ $priceOld = $product->setPrice($prodArr->price);
378
+ }
379
+
380
+ /*if (isset($spPrice)) {
381
+ $priceOld = $product->setSpecialPrice($spPrice);
382
+ }*/
383
+
384
+ $product->save();
385
+ $responseArr[]=array('sku'=>$prodArr->sku,'error_code'=>'0');
386
+ $sucessCounter++;
387
+ }else{
388
+ $responseArr[]=array('sku'=>$prodArr->sku,'error_code'=>'333','error_details'=>'SKU could not be loaded');
389
+ $failedCounter++;
390
+ }
391
+ } catch (Exception $exc) {
392
+ $responseArr[]=array('sku'=>$prodArr->sku,'error_code'=>'444','error_details'=>$exc->getMessage());
393
+ $failedCounter++;
394
+ }
395
+
396
+
397
+ }
398
+
399
+ $returnArr=array();
400
+ $returnArr['ResultData']= $responseArr;
401
+ $returnArr['Succeeded']=$sucessCounter;
402
+ $returnArr['Failed']=$failedCounter;
403
+ echo json_encode($returnArr);
404
+ die;
405
+ }
406
+
407
+ public function generatejasonAction(){
408
+ $result['products']=array(
409
+ array(
410
+ 'sku'=>'zol_r_sm',
411
+ 'price'=>'303.3'
412
+ ),
413
+ array(
414
+ 'sku'=>'4fasd5f5',
415
+ 'price'=>'770.0'
416
+ )
417
+ );
418
+ echo json_encode($result['products']);
419
+ die;
420
+ }
421
+
422
+ public function pingAction(){
423
+
424
+ $result['storeurl']=Mage::getUrl();
425
+ echo json_encode($result);
426
+ die;
427
+ }
428
+
429
+ }
430
+ ?>
app/code/local/Wisepricer/Syncer/etc/config.xml ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Wisepricer_Syncer>
5
+ <version>1.1.2.1</version>
6
+ <url>http://www.wisepricer.com/index.php</url>
7
+ <modulename>Wisepricer Syncer</modulename>
8
+ </Wisepricer_Syncer>
9
+ </modules>
10
+ <syncer>
11
+ <version>1.2</version>
12
+ </syncer>
13
+ <global>
14
+ <models>
15
+ <wisepricer_syncer>
16
+ <class>Wisepricer_Syncer_Model</class>
17
+ <resourceModel>wisepricer_syncer_mysql4</resourceModel>
18
+ </wisepricer_syncer>
19
+ <wisepricer_syncer_mysql4>
20
+ <class>Wisepricer_Syncer_Model_Mysql4</class>
21
+ <entities>
22
+ <wisepricer_syncer_config>
23
+ <table>wisepricer_syncer_config</table>
24
+ </wisepricer_syncer_config>
25
+ <wisepricer_syncer_mapping>
26
+ <table>wisepricer_syncer_mapping</table>
27
+ </wisepricer_syncer_mapping>
28
+ </entities>
29
+ </wisepricer_syncer_mysql4>
30
+ </models>
31
+ <resources>
32
+ <syncer_setup>
33
+ <setup>
34
+ <module>Wisepricer_Syncer</module>
35
+ </setup>
36
+ <connection>
37
+ <use>core_setup</use>
38
+ </connection>
39
+ </syncer_setup>
40
+ <syncer_write>
41
+ <connection>
42
+ <use>core_write</use>
43
+ </connection>
44
+ </syncer_write>
45
+ <syncer_read>
46
+ <connection>
47
+ <use>core_read</use>
48
+ </connection>
49
+ </syncer_read>
50
+ </resources>
51
+ <blocks>
52
+ <syncer>
53
+ <class>Wisepricer_Syncer_Block</class>
54
+ </syncer>
55
+ </blocks>
56
+ <helpers>
57
+ <syncer>
58
+ <class>Wisepricer_Syncer_Helper</class>
59
+ </syncer>
60
+ </helpers>
61
+ </global>
62
+ <frontend>
63
+ <routers>
64
+ <wisepricer>
65
+ <use>standard</use>
66
+ <args>
67
+ <module>Wisepricer_Syncer</module>
68
+ <frontName>wisepricer</frontName>
69
+ </args>
70
+ </wisepricer>
71
+ </routers>
72
+ </frontend>
73
+ <admin>
74
+ <routers>
75
+ <adminhtml>
76
+ <args>
77
+ <modules>
78
+ <Wisepricer_Syncer_Adminhtml before="Mage_Adminhtml">Wisepricer_Syncer_Adminhtml</Wisepricer_Syncer_Adminhtml>
79
+ </modules>
80
+ </args>
81
+ </adminhtml>
82
+ </routers>
83
+ </admin>
84
+ <adminhtml>
85
+ <acl>
86
+ <resources>
87
+ <admin>
88
+ <children>
89
+ <system>
90
+ <children>
91
+ <config>
92
+ <children>
93
+ <syncer>
94
+ <title>Wisepricer Configuration</title>
95
+ </syncer>
96
+ </children>
97
+ </config>
98
+ </children>
99
+ </system>
100
+ </children>
101
+ </admin>
102
+ </resources>
103
+ </acl>
104
+ <layout>
105
+ <updates>
106
+ <wisepricer_syncer>
107
+ <file>syncer.xml</file>
108
+ </wisepricer_syncer>
109
+ </updates>
110
+ </layout>
111
+ <menu>
112
+ <wisepricer>
113
+ <title>Wisepricer</title>
114
+ <sort_order>70</sort_order>
115
+ <action>adminhtml/syncer/mapping</action>
116
+ </wisepricer>
117
+ </menu>
118
+ </adminhtml>
119
+ <default>
120
+ <syncer>
121
+ <config>
122
+ <ftpport>21</ftpport>
123
+ </config>
124
+ </syncer>
125
+ </default>
126
+ </config>
app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/AES.php ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of AES.
6
+ *
7
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
12
+ * {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
13
+ * it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()}
14
+ * is called, again, at which point, it'll be recalculated.
15
+ *
16
+ * Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't
17
+ * make a whole lot of sense. {@link Crypt_AES::setBlockLength() setBlockLength()}, for instance. Calling that function,
18
+ * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
19
+ *
20
+ * Here's a short example of how to use this library:
21
+ * <code>
22
+ * <?php
23
+ * include('Crypt/AES.php');
24
+ *
25
+ * $aes = new Crypt_AES();
26
+ *
27
+ * $aes->setKey('abcdefghijklmnop');
28
+ *
29
+ * $size = 10 * 1024;
30
+ * $plaintext = '';
31
+ * for ($i = 0; $i < $size; $i++) {
32
+ * $plaintext.= 'a';
33
+ * }
34
+ *
35
+ * echo $aes->decrypt($aes->encrypt($plaintext));
36
+ * ?>
37
+ * </code>
38
+ *
39
+ * LICENSE: This library is free software; you can redistribute it and/or
40
+ * modify it under the terms of the GNU Lesser General Public
41
+ * License as published by the Free Software Foundation; either
42
+ * version 2.1 of the License, or (at your option) any later version.
43
+ *
44
+ * This library is distributed in the hope that it will be useful,
45
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
46
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47
+ * Lesser General Public License for more details.
48
+ *
49
+ * You should have received a copy of the GNU Lesser General Public
50
+ * License along with this library; if not, write to the Free Software
51
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
52
+ * MA 02111-1307 USA
53
+ *
54
+ * @category Crypt
55
+ * @package Crypt_AES
56
+ * @author Jim Wigginton <terrafrost@php.net>
57
+ * @copyright MMVIII Jim Wigginton
58
+ * @license http://www.gnu.org/licenses/lgpl.txt
59
+ * @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $
60
+ * @link http://phpseclib.sourceforge.net
61
+ */
62
+
63
+ /**
64
+ * Include Crypt_Rijndael
65
+ */
66
+ require_once 'Rijndael.php';
67
+
68
+ /**#@+
69
+ * @access public
70
+ * @see Crypt_AES::encrypt()
71
+ * @see Crypt_AES::decrypt()
72
+ */
73
+ /**
74
+ * Encrypt / decrypt using the Counter mode.
75
+ *
76
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
77
+ *
78
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
79
+ */
80
+ define('CRYPT_AES_MODE_CTR', -1);
81
+ /**
82
+ * Encrypt / decrypt using the Electronic Code Book mode.
83
+ *
84
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
85
+ */
86
+ define('CRYPT_AES_MODE_ECB', 1);
87
+ /**
88
+ * Encrypt / decrypt using the Code Book Chaining mode.
89
+ *
90
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
91
+ */
92
+ define('CRYPT_AES_MODE_CBC', 2);
93
+ /**#@-*/
94
+
95
+ /**#@+
96
+ * @access private
97
+ * @see Crypt_AES::Crypt_AES()
98
+ */
99
+ /**
100
+ * Toggles the internal implementation
101
+ */
102
+ define('CRYPT_AES_MODE_INTERNAL', 1);
103
+ /**
104
+ * Toggles the mcrypt implementation
105
+ */
106
+ define('CRYPT_AES_MODE_MCRYPT', 2);
107
+ /**#@-*/
108
+
109
+ /**
110
+ * Pure-PHP implementation of AES.
111
+ *
112
+ * @author Jim Wigginton <terrafrost@php.net>
113
+ * @version 0.1.0
114
+ * @access public
115
+ * @package Crypt_AES
116
+ */
117
+ class Crypt_AES extends Crypt_Rijndael {
118
+ /**
119
+ * mcrypt resource for encryption
120
+ *
121
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
122
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
123
+ *
124
+ * @see Crypt_AES::encrypt()
125
+ * @var String
126
+ * @access private
127
+ */
128
+ var $enmcrypt;
129
+
130
+ /**
131
+ * mcrypt resource for decryption
132
+ *
133
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
134
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
135
+ *
136
+ * @see Crypt_AES::decrypt()
137
+ * @var String
138
+ * @access private
139
+ */
140
+ var $demcrypt;
141
+
142
+ /**
143
+ * Default Constructor.
144
+ *
145
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
146
+ * CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used.
147
+ *
148
+ * @param optional Integer $mode
149
+ * @return Crypt_AES
150
+ * @access public
151
+ */
152
+ function Crypt_AES($mode = CRYPT_AES_MODE_CBC)
153
+ {
154
+ if ( !defined('CRYPT_AES_MODE') ) {
155
+ switch (true) {
156
+ case extension_loaded('mcrypt'):
157
+ // i'd check to see if aes was supported, by doing in_array('des', mcrypt_list_algorithms('')),
158
+ // but since that can be changed after the object has been created, there doesn't seem to be
159
+ // a lot of point...
160
+ define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT);
161
+ break;
162
+ default:
163
+ define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL);
164
+ }
165
+ }
166
+
167
+ switch ( CRYPT_AES_MODE ) {
168
+ case CRYPT_AES_MODE_MCRYPT:
169
+ switch ($mode) {
170
+ case CRYPT_AES_MODE_ECB:
171
+ $this->mode = MCRYPT_MODE_ECB;
172
+ break;
173
+ case CRYPT_AES_MODE_CTR:
174
+ // ctr doesn't have a constant associated with it even though it appears to be fairly widely
175
+ // supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to
176
+ // include a compatibility layer. the layer has been implemented but, for now, is commented out.
177
+ $this->mode = 'ctr';
178
+ //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
179
+ break;
180
+ case CRYPT_AES_MODE_CBC:
181
+ default:
182
+ $this->mode = MCRYPT_MODE_CBC;
183
+ }
184
+
185
+ break;
186
+ default:
187
+ switch ($mode) {
188
+ case CRYPT_AES_MODE_ECB:
189
+ $this->mode = CRYPT_RIJNDAEL_MODE_ECB;
190
+ break;
191
+ case CRYPT_AES_MODE_CTR:
192
+ $this->mode = CRYPT_RIJNDAEL_MODE_CTR;
193
+ break;
194
+ case CRYPT_AES_MODE_CBC:
195
+ default:
196
+ $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
197
+ }
198
+ }
199
+
200
+ if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) {
201
+ parent::Crypt_Rijndael($this->mode);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Dummy function
207
+ *
208
+ * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
209
+ *
210
+ * @access public
211
+ * @param Integer $length
212
+ */
213
+ function setBlockLength($length)
214
+ {
215
+ return;
216
+ }
217
+
218
+ /**
219
+ * Encrypts a message.
220
+ *
221
+ * $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the
222
+ * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
223
+ * URL:
224
+ *
225
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
226
+ *
227
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
228
+ * strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that
229
+ * length.
230
+ *
231
+ * @see Crypt_AES::decrypt()
232
+ * @access public
233
+ * @param String $plaintext
234
+ */
235
+ function encrypt($plaintext)
236
+ {
237
+ if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
238
+ $this->_mcryptSetup();
239
+ /*
240
+ if ($this->mode == CRYPT_AES_MODE_CTR) {
241
+ $iv = $this->encryptIV;
242
+ $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($plaintext), $iv));
243
+ $ciphertext = $plaintext ^ $xor;
244
+ if ($this->continuousBuffer) {
245
+ $this->encryptIV = $iv;
246
+ }
247
+ return $ciphertext;
248
+ }
249
+ */
250
+
251
+ if ($this->mode != 'ctr') {
252
+ $plaintext = $this->_pad($plaintext);
253
+ }
254
+
255
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
256
+
257
+ if (!$this->continuousBuffer) {
258
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
259
+ }
260
+
261
+ return $ciphertext;
262
+ }
263
+
264
+ return parent::encrypt($plaintext);
265
+ }
266
+
267
+ /**
268
+ * Decrypts a message.
269
+ *
270
+ * If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is.
271
+ *
272
+ * @see Crypt_AES::encrypt()
273
+ * @access public
274
+ * @param String $ciphertext
275
+ */
276
+ function decrypt($ciphertext)
277
+ {
278
+ if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
279
+ $this->_mcryptSetup();
280
+ /*
281
+ if ($this->mode == CRYPT_AES_MODE_CTR) {
282
+ $iv = $this->decryptIV;
283
+ $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($ciphertext), $iv));
284
+ $plaintext = $ciphertext ^ $xor;
285
+ if ($this->continuousBuffer) {
286
+ $this->decryptIV = $iv;
287
+ }
288
+ return $plaintext;
289
+ }
290
+ */
291
+
292
+ if ($this->mode != 'ctr') {
293
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
294
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
295
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));
296
+ }
297
+
298
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
299
+
300
+ if (!$this->continuousBuffer) {
301
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
302
+ }
303
+
304
+ return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
305
+ }
306
+
307
+ return parent::decrypt($ciphertext);
308
+ }
309
+
310
+ /**
311
+ * Setup mcrypt
312
+ *
313
+ * Validates all the variables.
314
+ *
315
+ * @access private
316
+ */
317
+ function _mcryptSetup()
318
+ {
319
+ if (!$this->changed) {
320
+ return;
321
+ }
322
+
323
+ if (!$this->explicit_key_length) {
324
+ // this just copied from Crypt_Rijndael::_setup()
325
+ $length = strlen($this->key) >> 2;
326
+ if ($length > 8) {
327
+ $length = 8;
328
+ } else if ($length < 4) {
329
+ $length = 4;
330
+ }
331
+ $this->Nk = $length;
332
+ $this->key_size = $length << 2;
333
+ }
334
+
335
+ switch ($this->Nk) {
336
+ case 4: // 128
337
+ $this->key_size = 16;
338
+ break;
339
+ case 5: // 160
340
+ case 6: // 192
341
+ $this->key_size = 24;
342
+ break;
343
+ case 7: // 224
344
+ case 8: // 256
345
+ $this->key_size = 32;
346
+ }
347
+
348
+ $this->key = substr($this->key, 0, $this->key_size);
349
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
350
+
351
+ if (!isset($this->enmcrypt)) {
352
+ $mode = $this->mode;
353
+ //$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;
354
+
355
+ $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
356
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
357
+ } // else should mcrypt_generic_deinit be called?
358
+
359
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
360
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
361
+
362
+ $this->changed = false;
363
+ }
364
+
365
+ /**
366
+ * Encrypts a block
367
+ *
368
+ * Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
369
+ *
370
+ * @see Crypt_Rijndael::_encryptBlock()
371
+ * @access private
372
+ * @param String $in
373
+ * @return String
374
+ */
375
+ function _encryptBlock($in)
376
+ {
377
+ $state = unpack('N*word', $in);
378
+
379
+ $Nr = $this->Nr;
380
+ $w = $this->w;
381
+ $t0 = $this->t0;
382
+ $t1 = $this->t1;
383
+ $t2 = $this->t2;
384
+ $t3 = $this->t3;
385
+
386
+ // addRoundKey and reindex $state
387
+ $state = array(
388
+ $state['word1'] ^ $w[0][0],
389
+ $state['word2'] ^ $w[0][1],
390
+ $state['word3'] ^ $w[0][2],
391
+ $state['word4'] ^ $w[0][3]
392
+ );
393
+
394
+ // shiftRows + subWord + mixColumns + addRoundKey
395
+ // we could loop unroll this and use if statements to do more rounds as necessary, but, in my tests, that yields
396
+ // only a marginal improvement. since that also, imho, hinders the readability of the code, i've opted not to do it.
397
+ for ($round = 1; $round < $this->Nr; $round++) {
398
+ $state = array(
399
+ $t0[$state[0] & 0xFF000000] ^ $t1[$state[1] & 0x00FF0000] ^ $t2[$state[2] & 0x0000FF00] ^ $t3[$state[3] & 0x000000FF] ^ $w[$round][0],
400
+ $t0[$state[1] & 0xFF000000] ^ $t1[$state[2] & 0x00FF0000] ^ $t2[$state[3] & 0x0000FF00] ^ $t3[$state[0] & 0x000000FF] ^ $w[$round][1],
401
+ $t0[$state[2] & 0xFF000000] ^ $t1[$state[3] & 0x00FF0000] ^ $t2[$state[0] & 0x0000FF00] ^ $t3[$state[1] & 0x000000FF] ^ $w[$round][2],
402
+ $t0[$state[3] & 0xFF000000] ^ $t1[$state[0] & 0x00FF0000] ^ $t2[$state[1] & 0x0000FF00] ^ $t3[$state[2] & 0x000000FF] ^ $w[$round][3]
403
+ );
404
+
405
+ }
406
+
407
+ // subWord
408
+ $state = array(
409
+ $this->_subWord($state[0]),
410
+ $this->_subWord($state[1]),
411
+ $this->_subWord($state[2]),
412
+ $this->_subWord($state[3])
413
+ );
414
+
415
+ // shiftRows + addRoundKey
416
+ $state = array(
417
+ ($state[0] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[3] & 0x000000FF) ^ $this->w[$this->Nr][0],
418
+ ($state[1] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[0] & 0x000000FF) ^ $this->w[$this->Nr][1],
419
+ ($state[2] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[1] & 0x000000FF) ^ $this->w[$this->Nr][2],
420
+ ($state[3] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[2] & 0x000000FF) ^ $this->w[$this->Nr][3]
421
+ );
422
+
423
+ return pack('N*', $state[0], $state[1], $state[2], $state[3]);
424
+ }
425
+
426
+ /**
427
+ * Decrypts a block
428
+ *
429
+ * Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
430
+ *
431
+ * @see Crypt_Rijndael::_decryptBlock()
432
+ * @access private
433
+ * @param String $in
434
+ * @return String
435
+ */
436
+ function _decryptBlock($in)
437
+ {
438
+ $state = unpack('N*word', $in);
439
+
440
+ $Nr = $this->Nr;
441
+ $dw = $this->dw;
442
+ $dt0 = $this->dt0;
443
+ $dt1 = $this->dt1;
444
+ $dt2 = $this->dt2;
445
+ $dt3 = $this->dt3;
446
+
447
+ // addRoundKey and reindex $state
448
+ $state = array(
449
+ $state['word1'] ^ $dw[$this->Nr][0],
450
+ $state['word2'] ^ $dw[$this->Nr][1],
451
+ $state['word3'] ^ $dw[$this->Nr][2],
452
+ $state['word4'] ^ $dw[$this->Nr][3]
453
+ );
454
+
455
+
456
+ // invShiftRows + invSubBytes + invMixColumns + addRoundKey
457
+ for ($round = $this->Nr - 1; $round > 0; $round--) {
458
+ $state = array(
459
+ $dt0[$state[0] & 0xFF000000] ^ $dt1[$state[3] & 0x00FF0000] ^ $dt2[$state[2] & 0x0000FF00] ^ $dt3[$state[1] & 0x000000FF] ^ $dw[$round][0],
460
+ $dt0[$state[1] & 0xFF000000] ^ $dt1[$state[0] & 0x00FF0000] ^ $dt2[$state[3] & 0x0000FF00] ^ $dt3[$state[2] & 0x000000FF] ^ $dw[$round][1],
461
+ $dt0[$state[2] & 0xFF000000] ^ $dt1[$state[1] & 0x00FF0000] ^ $dt2[$state[0] & 0x0000FF00] ^ $dt3[$state[3] & 0x000000FF] ^ $dw[$round][2],
462
+ $dt0[$state[3] & 0xFF000000] ^ $dt1[$state[2] & 0x00FF0000] ^ $dt2[$state[1] & 0x0000FF00] ^ $dt3[$state[0] & 0x000000FF] ^ $dw[$round][3]
463
+ );
464
+ }
465
+
466
+ // invShiftRows + invSubWord + addRoundKey
467
+ $state = array(
468
+ $this->_invSubWord(($state[0] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[1] & 0x000000FF)) ^ $dw[0][0],
469
+ $this->_invSubWord(($state[1] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[2] & 0x000000FF)) ^ $dw[0][1],
470
+ $this->_invSubWord(($state[2] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[3] & 0x000000FF)) ^ $dw[0][2],
471
+ $this->_invSubWord(($state[3] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[0] & 0x000000FF)) ^ $dw[0][3]
472
+ );
473
+
474
+ return pack('N*', $state[0], $state[1], $state[2], $state[3]);
475
+ }
476
+ }
477
+
478
+ // vim: ts=4:sw=4:et:
479
+ // vim6: fdl=1:
app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/DES.php ADDED
@@ -0,0 +1,945 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of DES.
6
+ *
7
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * Useful resources are as follows:
12
+ *
13
+ * - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material}
14
+ * - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard}
15
+ * - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example}
16
+ *
17
+ * Here's a short example of how to use this library:
18
+ * <code>
19
+ * <?php
20
+ * include('Crypt/DES.php');
21
+ *
22
+ * $des = new Crypt_DES();
23
+ *
24
+ * $des->setKey('abcdefgh');
25
+ *
26
+ * $size = 10 * 1024;
27
+ * $plaintext = '';
28
+ * for ($i = 0; $i < $size; $i++) {
29
+ * $plaintext.= 'a';
30
+ * }
31
+ *
32
+ * echo $des->decrypt($des->encrypt($plaintext));
33
+ * ?>
34
+ * </code>
35
+ *
36
+ * LICENSE: This library is free software; you can redistribute it and/or
37
+ * modify it under the terms of the GNU Lesser General Public
38
+ * License as published by the Free Software Foundation; either
39
+ * version 2.1 of the License, or (at your option) any later version.
40
+ *
41
+ * This library is distributed in the hope that it will be useful,
42
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
43
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44
+ * Lesser General Public License for more details.
45
+ *
46
+ * You should have received a copy of the GNU Lesser General Public
47
+ * License along with this library; if not, write to the Free Software
48
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
49
+ * MA 02111-1307 USA
50
+ *
51
+ * @category Crypt
52
+ * @package Crypt_DES
53
+ * @author Jim Wigginton <terrafrost@php.net>
54
+ * @copyright MMVII Jim Wigginton
55
+ * @license http://www.gnu.org/licenses/lgpl.txt
56
+ * @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
57
+ * @link http://phpseclib.sourceforge.net
58
+ */
59
+
60
+ /**#@+
61
+ * @access private
62
+ * @see Crypt_DES::_prepareKey()
63
+ * @see Crypt_DES::_processBlock()
64
+ */
65
+ /**
66
+ * Contains array_reverse($keys[CRYPT_DES_DECRYPT])
67
+ */
68
+ define('CRYPT_DES_ENCRYPT', 0);
69
+ /**
70
+ * Contains array_reverse($keys[CRYPT_DES_ENCRYPT])
71
+ */
72
+ define('CRYPT_DES_DECRYPT', 1);
73
+ /**#@-*/
74
+
75
+ /**#@+
76
+ * @access public
77
+ * @see Crypt_DES::encrypt()
78
+ * @see Crypt_DES::decrypt()
79
+ */
80
+ /**
81
+ * Encrypt / decrypt using the Counter mode.
82
+ *
83
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
84
+ *
85
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
86
+ */
87
+ define('CRYPT_DES_MODE_CTR', -1);
88
+ /**
89
+ * Encrypt / decrypt using the Electronic Code Book mode.
90
+ *
91
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
92
+ */
93
+ define('CRYPT_DES_MODE_ECB', 1);
94
+ /**
95
+ * Encrypt / decrypt using the Code Book Chaining mode.
96
+ *
97
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
98
+ */
99
+ define('CRYPT_DES_MODE_CBC', 2);
100
+ /**#@-*/
101
+
102
+ /**#@+
103
+ * @access private
104
+ * @see Crypt_DES::Crypt_DES()
105
+ */
106
+ /**
107
+ * Toggles the internal implementation
108
+ */
109
+ define('CRYPT_DES_MODE_INTERNAL', 1);
110
+ /**
111
+ * Toggles the mcrypt implementation
112
+ */
113
+ define('CRYPT_DES_MODE_MCRYPT', 2);
114
+ /**#@-*/
115
+
116
+ /**
117
+ * Pure-PHP implementation of DES.
118
+ *
119
+ * @author Jim Wigginton <terrafrost@php.net>
120
+ * @version 0.1.0
121
+ * @access public
122
+ * @package Crypt_DES
123
+ */
124
+ class Crypt_DES {
125
+ /**
126
+ * The Key Schedule
127
+ *
128
+ * @see Crypt_DES::setKey()
129
+ * @var Array
130
+ * @access private
131
+ */
132
+ var $keys = "\0\0\0\0\0\0\0\0";
133
+
134
+ /**
135
+ * The Encryption Mode
136
+ *
137
+ * @see Crypt_DES::Crypt_DES()
138
+ * @var Integer
139
+ * @access private
140
+ */
141
+ var $mode;
142
+
143
+ /**
144
+ * Continuous Buffer status
145
+ *
146
+ * @see Crypt_DES::enableContinuousBuffer()
147
+ * @var Boolean
148
+ * @access private
149
+ */
150
+ var $continuousBuffer = false;
151
+
152
+ /**
153
+ * Padding status
154
+ *
155
+ * @see Crypt_DES::enablePadding()
156
+ * @var Boolean
157
+ * @access private
158
+ */
159
+ var $padding = true;
160
+
161
+ /**
162
+ * The Initialization Vector
163
+ *
164
+ * @see Crypt_DES::setIV()
165
+ * @var String
166
+ * @access private
167
+ */
168
+ var $iv = "\0\0\0\0\0\0\0\0";
169
+
170
+ /**
171
+ * A "sliding" Initialization Vector
172
+ *
173
+ * @see Crypt_DES::enableContinuousBuffer()
174
+ * @var String
175
+ * @access private
176
+ */
177
+ var $encryptIV = "\0\0\0\0\0\0\0\0";
178
+
179
+ /**
180
+ * A "sliding" Initialization Vector
181
+ *
182
+ * @see Crypt_DES::enableContinuousBuffer()
183
+ * @var String
184
+ * @access private
185
+ */
186
+ var $decryptIV = "\0\0\0\0\0\0\0\0";
187
+
188
+ /**
189
+ * mcrypt resource for encryption
190
+ *
191
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
192
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
193
+ *
194
+ * @see Crypt_AES::encrypt()
195
+ * @var String
196
+ * @access private
197
+ */
198
+ var $enmcrypt;
199
+
200
+ /**
201
+ * mcrypt resource for decryption
202
+ *
203
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
204
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
205
+ *
206
+ * @see Crypt_AES::decrypt()
207
+ * @var String
208
+ * @access private
209
+ */
210
+ var $demcrypt;
211
+
212
+ /**
213
+ * Does the (en|de)mcrypt resource need to be (re)initialized?
214
+ *
215
+ * @see setKey()
216
+ * @see setIV()
217
+ * @var Boolean
218
+ * @access private
219
+ */
220
+ var $changed = true;
221
+
222
+ /**
223
+ * Default Constructor.
224
+ *
225
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
226
+ * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
227
+ *
228
+ * @param optional Integer $mode
229
+ * @return Crypt_DES
230
+ * @access public
231
+ */
232
+ function Crypt_DES($mode = CRYPT_MODE_DES_CBC)
233
+ {
234
+ if ( !defined('CRYPT_DES_MODE') ) {
235
+ switch (true) {
236
+ case extension_loaded('mcrypt'):
237
+ // i'd check to see if des was supported, by doing in_array('des', mcrypt_list_algorithms('')),
238
+ // but since that can be changed after the object has been created, there doesn't seem to be
239
+ // a lot of point...
240
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
241
+ break;
242
+ default:
243
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
244
+ }
245
+ }
246
+
247
+ switch ( CRYPT_DES_MODE ) {
248
+ case CRYPT_DES_MODE_MCRYPT:
249
+ switch ($mode) {
250
+ case CRYPT_DES_MODE_ECB:
251
+ $this->mode = MCRYPT_MODE_ECB;
252
+ break;
253
+ case CRYPT_DES_MODE_CTR:
254
+ $this->mode = 'ctr';
255
+ //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR;
256
+ break;
257
+ case CRYPT_DES_MODE_CBC:
258
+ default:
259
+ $this->mode = MCRYPT_MODE_CBC;
260
+ }
261
+
262
+ break;
263
+ default:
264
+ switch ($mode) {
265
+ case CRYPT_DES_MODE_ECB:
266
+ case CRYPT_DES_MODE_CTR:
267
+ case CRYPT_DES_MODE_CBC:
268
+ $this->mode = $mode;
269
+ break;
270
+ default:
271
+ $this->mode = CRYPT_DES_MODE_CBC;
272
+ }
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Sets the key.
278
+ *
279
+ * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we
280
+ * only use the first eight, if $key has more then eight characters in it, and pad $key with the
281
+ * null byte if it is less then eight characters long.
282
+ *
283
+ * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
284
+ *
285
+ * If the key is not explicitly set, it'll be assumed to be all zero's.
286
+ *
287
+ * @access public
288
+ * @param String $key
289
+ */
290
+ function setKey($key)
291
+ {
292
+ $this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? substr($key, 0, 8) : $this->_prepareKey($key);
293
+ $this->changed = true;
294
+ }
295
+
296
+ /**
297
+ * Sets the initialization vector. (optional)
298
+ *
299
+ * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
300
+ * to be all zero's.
301
+ *
302
+ * @access public
303
+ * @param String $iv
304
+ */
305
+ function setIV($iv)
306
+ {
307
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
308
+ $this->changed = true;
309
+ }
310
+
311
+ /**
312
+ * Generate CTR XOR encryption key
313
+ *
314
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
315
+ * plaintext / ciphertext in CTR mode.
316
+ *
317
+ * @see Crypt_DES::decrypt()
318
+ * @see Crypt_DES::encrypt()
319
+ * @access public
320
+ * @param Integer $length
321
+ * @param String $iv
322
+ */
323
+ function _generate_xor($length, &$iv)
324
+ {
325
+ $xor = '';
326
+ $num_blocks = ($length + 7) >> 3;
327
+ for ($i = 0; $i < $num_blocks; $i++) {
328
+ $xor.= $iv;
329
+ for ($j = 4; $j <= 8; $j+=4) {
330
+ $temp = substr($iv, -$j, 4);
331
+ switch ($temp) {
332
+ case "\xFF\xFF\xFF\xFF":
333
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
334
+ break;
335
+ case "\x7F\xFF\xFF\xFF":
336
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
337
+ break 2;
338
+ default:
339
+ extract(unpack('Ncount', $temp));
340
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
341
+ break 2;
342
+ }
343
+ }
344
+ }
345
+
346
+ return $xor;
347
+ }
348
+
349
+ /**
350
+ * Encrypts a message.
351
+ *
352
+ * $plaintext will be padded with up to 8 additional bytes. Other DES implementations may or may not pad in the
353
+ * same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
354
+ * URL:
355
+ *
356
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
357
+ *
358
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
359
+ * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
360
+ * length.
361
+ *
362
+ * @see Crypt_DES::decrypt()
363
+ * @access public
364
+ * @param String $plaintext
365
+ */
366
+ function encrypt($plaintext)
367
+ {
368
+ if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
369
+ $plaintext = $this->_pad($plaintext);
370
+ }
371
+
372
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
373
+ if ($this->changed) {
374
+ if (!isset($this->enmcrypt)) {
375
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
376
+ }
377
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
378
+ $this->changed = false;
379
+ }
380
+
381
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
382
+
383
+ if (!$this->continuousBuffer) {
384
+ mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
385
+ }
386
+
387
+ return $ciphertext;
388
+ }
389
+
390
+ if (!is_array($this->keys)) {
391
+ $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
392
+ }
393
+
394
+ $ciphertext = '';
395
+ switch ($this->mode) {
396
+ case CRYPT_DES_MODE_ECB:
397
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
398
+ $ciphertext.= $this->_processBlock(substr($plaintext, $i, 8), CRYPT_DES_ENCRYPT);
399
+ }
400
+ break;
401
+ case CRYPT_DES_MODE_CBC:
402
+ $xor = $this->encryptIV;
403
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
404
+ $block = substr($plaintext, $i, 8);
405
+ $block = $this->_processBlock($block ^ $xor, CRYPT_DES_ENCRYPT);
406
+ $xor = $block;
407
+ $ciphertext.= $block;
408
+ }
409
+ if ($this->continuousBuffer) {
410
+ $this->encryptIV = $xor;
411
+ }
412
+ break;
413
+ case CRYPT_DES_MODE_CTR:
414
+ $xor = $this->encryptIV;
415
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
416
+ $block = substr($plaintext, $i, 8);
417
+ $key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
418
+ $ciphertext.= $block ^ $key;
419
+ }
420
+ if ($this->continuousBuffer) {
421
+ $this->encryptIV = $xor;
422
+ }
423
+ }
424
+
425
+ return $ciphertext;
426
+ }
427
+
428
+ /**
429
+ * Decrypts a message.
430
+ *
431
+ * If strlen($ciphertext) is not a multiple of 8, null bytes will be added to the end of the string until it is.
432
+ *
433
+ * @see Crypt_DES::encrypt()
434
+ * @access public
435
+ * @param String $ciphertext
436
+ */
437
+ function decrypt($ciphertext)
438
+ {
439
+ if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
440
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
441
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
442
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
443
+ }
444
+
445
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
446
+ if ($this->changed) {
447
+ if (!isset($this->demcrypt)) {
448
+ $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
449
+ }
450
+ mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
451
+ $this->changed = false;
452
+ }
453
+
454
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
455
+
456
+ if (!$this->continuousBuffer) {
457
+ mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
458
+ }
459
+
460
+ return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
461
+ }
462
+
463
+ if (!is_array($this->keys)) {
464
+ $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
465
+ }
466
+
467
+ $plaintext = '';
468
+ switch ($this->mode) {
469
+ case CRYPT_DES_MODE_ECB:
470
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
471
+ $plaintext.= $this->_processBlock(substr($ciphertext, $i, 8), CRYPT_DES_DECRYPT);
472
+ }
473
+ break;
474
+ case CRYPT_DES_MODE_CBC:
475
+ $xor = $this->decryptIV;
476
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
477
+ $block = substr($ciphertext, $i, 8);
478
+ $plaintext.= $this->_processBlock($block, CRYPT_DES_DECRYPT) ^ $xor;
479
+ $xor = $block;
480
+ }
481
+ if ($this->continuousBuffer) {
482
+ $this->decryptIV = $xor;
483
+ }
484
+ break;
485
+ case CRYPT_DES_MODE_CTR:
486
+ $xor = $this->decryptIV;
487
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
488
+ $block = substr($ciphertext, $i, 8);
489
+ $key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
490
+ $plaintext.= $block ^ $key;
491
+ }
492
+ if ($this->continuousBuffer) {
493
+ $this->decryptIV = $xor;
494
+ }
495
+ }
496
+
497
+ return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
498
+ }
499
+
500
+ /**
501
+ * Treat consecutive "packets" as if they are a continuous buffer.
502
+ *
503
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
504
+ * will yield different outputs:
505
+ *
506
+ * <code>
507
+ * echo $des->encrypt(substr($plaintext, 0, 8));
508
+ * echo $des->encrypt(substr($plaintext, 8, 8));
509
+ * </code>
510
+ * <code>
511
+ * echo $des->encrypt($plaintext);
512
+ * </code>
513
+ *
514
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
515
+ * another, as demonstrated with the following:
516
+ *
517
+ * <code>
518
+ * $des->encrypt(substr($plaintext, 0, 8));
519
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
520
+ * </code>
521
+ * <code>
522
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
523
+ * </code>
524
+ *
525
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
526
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
527
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
528
+ *
529
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
530
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
531
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
532
+ * however, they are also less intuitive and more likely to cause you problems.
533
+ *
534
+ * @see Crypt_DES::disableContinuousBuffer()
535
+ * @access public
536
+ */
537
+ function enableContinuousBuffer()
538
+ {
539
+ $this->continuousBuffer = true;
540
+ }
541
+
542
+ /**
543
+ * Treat consecutive packets as if they are a discontinuous buffer.
544
+ *
545
+ * The default behavior.
546
+ *
547
+ * @see Crypt_DES::enableContinuousBuffer()
548
+ * @access public
549
+ */
550
+ function disableContinuousBuffer()
551
+ {
552
+ $this->continuousBuffer = false;
553
+ $this->encryptIV = $this->iv;
554
+ $this->decryptIV = $this->iv;
555
+ }
556
+
557
+ /**
558
+ * Pad "packets".
559
+ *
560
+ * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
561
+ * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
562
+ *
563
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
564
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
565
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
566
+ * transmitted separately)
567
+ *
568
+ * @see Crypt_DES::disablePadding()
569
+ * @access public
570
+ */
571
+ function enablePadding()
572
+ {
573
+ $this->padding = true;
574
+ }
575
+
576
+ /**
577
+ * Do not pad packets.
578
+ *
579
+ * @see Crypt_DES::enablePadding()
580
+ * @access public
581
+ */
582
+ function disablePadding()
583
+ {
584
+ $this->padding = false;
585
+ }
586
+
587
+ /**
588
+ * Pads a string
589
+ *
590
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
591
+ * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
592
+ *
593
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
594
+ * and padding will, hence forth, be enabled.
595
+ *
596
+ * @see Crypt_DES::_unpad()
597
+ * @access private
598
+ */
599
+ function _pad($text)
600
+ {
601
+ $length = strlen($text);
602
+
603
+ if (!$this->padding) {
604
+ if (($length & 7) == 0) {
605
+ return $text;
606
+ } else {
607
+ user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
608
+ $this->padding = true;
609
+ }
610
+ }
611
+
612
+ $pad = 8 - ($length & 7);
613
+ return str_pad($text, $length + $pad, chr($pad));
614
+ }
615
+
616
+ /**
617
+ * Unpads a string
618
+ *
619
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
620
+ * and false will be returned.
621
+ *
622
+ * @see Crypt_DES::_pad()
623
+ * @access private
624
+ */
625
+ function _unpad($text)
626
+ {
627
+ if (!$this->padding) {
628
+ return $text;
629
+ }
630
+
631
+ $length = ord($text[strlen($text) - 1]);
632
+
633
+ if (!$length || $length > 8) {
634
+ return false;
635
+ }
636
+
637
+ return substr($text, 0, -$length);
638
+ }
639
+
640
+ /**
641
+ * Encrypts or decrypts a 64-bit block
642
+ *
643
+ * $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT. See
644
+ * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general
645
+ * idea of what this function does.
646
+ *
647
+ * @access private
648
+ * @param String $block
649
+ * @param Integer $mode
650
+ * @return String
651
+ */
652
+ function _processBlock($block, $mode)
653
+ {
654
+ // s-boxes. in the official DES docs, they're described as being matrices that
655
+ // one accesses by using the first and last bits to determine the row and the
656
+ // middle four bits to determine the column. in this implementation, they've
657
+ // been converted to vectors
658
+ static $sbox = array(
659
+ array(
660
+ 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
661
+ 3, 10 ,10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
662
+ 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
663
+ 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13
664
+ ),
665
+ array(
666
+ 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
667
+ 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
668
+ 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
669
+ 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9
670
+ ),
671
+ array(
672
+ 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
673
+ 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
674
+ 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
675
+ 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12
676
+ ),
677
+ array(
678
+ 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
679
+ 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
680
+ 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
681
+ 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14
682
+ ),
683
+ array(
684
+ 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
685
+ 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
686
+ 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
687
+ 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3
688
+ ),
689
+ array(
690
+ 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
691
+ 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
692
+ 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
693
+ 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13
694
+ ),
695
+ array(
696
+ 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
697
+ 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
698
+ 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
699
+ 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12
700
+ ),
701
+ array(
702
+ 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
703
+ 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
704
+ 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
705
+ 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11
706
+ )
707
+ );
708
+
709
+ $keys = $this->keys;
710
+
711
+ $temp = unpack('Na/Nb', $block);
712
+ $block = array($temp['a'], $temp['b']);
713
+
714
+ // because php does arithmetic right shifts, if the most significant bits are set, right
715
+ // shifting those into the correct position will add 1's - not 0's. this will intefere
716
+ // with the | operation unless a second & is done. so we isolate these bits and left shift
717
+ // them into place. we then & each block with 0x7FFFFFFF to prevennt 1's from being added
718
+ // for any other shifts.
719
+ $msb = array(
720
+ ($block[0] >> 31) & 1,
721
+ ($block[1] >> 31) & 1
722
+ );
723
+ $block[0] &= 0x7FFFFFFF;
724
+ $block[1] &= 0x7FFFFFFF;
725
+
726
+ // we isolate the appropriate bit in the appropriate integer and shift as appropriate. in
727
+ // some cases, there are going to be multiple bits in the same integer that need to be shifted
728
+ // in the same way. we combine those into one shift operation.
729
+ $block = array(
730
+ (($block[1] & 0x00000040) << 25) | (($block[1] & 0x00004000) << 16) |
731
+ (($block[1] & 0x00400001) << 7) | (($block[1] & 0x40000100) >> 2) |
732
+ (($block[0] & 0x00000040) << 21) | (($block[0] & 0x00004000) << 12) |
733
+ (($block[0] & 0x00400001) << 3) | (($block[0] & 0x40000100) >> 6) |
734
+ (($block[1] & 0x00000010) << 19) | (($block[1] & 0x00001000) << 10) |
735
+ (($block[1] & 0x00100000) << 1) | (($block[1] & 0x10000000) >> 8) |
736
+ (($block[0] & 0x00000010) << 15) | (($block[0] & 0x00001000) << 6) |
737
+ (($block[0] & 0x00100000) >> 3) | (($block[0] & 0x10000000) >> 12) |
738
+ (($block[1] & 0x00000004) << 13) | (($block[1] & 0x00000400) << 4) |
739
+ (($block[1] & 0x00040000) >> 5) | (($block[1] & 0x04000000) >> 14) |
740
+ (($block[0] & 0x00000004) << 9) | ( $block[0] & 0x00000400 ) |
741
+ (($block[0] & 0x00040000) >> 9) | (($block[0] & 0x04000000) >> 18) |
742
+ (($block[1] & 0x00010000) >> 11) | (($block[1] & 0x01000000) >> 20) |
743
+ (($block[0] & 0x00010000) >> 15) | (($block[0] & 0x01000000) >> 24)
744
+ ,
745
+ (($block[1] & 0x00000080) << 24) | (($block[1] & 0x00008000) << 15) |
746
+ (($block[1] & 0x00800002) << 6) | (($block[0] & 0x00000080) << 20) |
747
+ (($block[0] & 0x00008000) << 11) | (($block[0] & 0x00800002) << 2) |
748
+ (($block[1] & 0x00000020) << 18) | (($block[1] & 0x00002000) << 9) |
749
+ ( $block[1] & 0x00200000 ) | (($block[1] & 0x20000000) >> 9) |
750
+ (($block[0] & 0x00000020) << 14) | (($block[0] & 0x00002000) << 5) |
751
+ (($block[0] & 0x00200000) >> 4) | (($block[0] & 0x20000000) >> 13) |
752
+ (($block[1] & 0x00000008) << 12) | (($block[1] & 0x00000800) << 3) |
753
+ (($block[1] & 0x00080000) >> 6) | (($block[1] & 0x08000000) >> 15) |
754
+ (($block[0] & 0x00000008) << 8) | (($block[0] & 0x00000800) >> 1) |
755
+ (($block[0] & 0x00080000) >> 10) | (($block[0] & 0x08000000) >> 19) |
756
+ (($block[1] & 0x00000200) >> 3) | (($block[0] & 0x00000200) >> 7) |
757
+ (($block[1] & 0x00020000) >> 12) | (($block[1] & 0x02000000) >> 21) |
758
+ (($block[0] & 0x00020000) >> 16) | (($block[0] & 0x02000000) >> 25) |
759
+ ($msb[1] << 28) | ($msb[0] << 24)
760
+ );
761
+
762
+ for ($i = 0; $i < 16; $i++) {
763
+ // start of "the Feistel (F) function" - see the following URL:
764
+ // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
765
+ $temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $keys[$mode][$i][0]]) << 28)
766
+ | (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $keys[$mode][$i][1]]) << 24)
767
+ | (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $keys[$mode][$i][2]]) << 20)
768
+ | (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $keys[$mode][$i][3]]) << 16)
769
+ | (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $keys[$mode][$i][4]]) << 12)
770
+ | (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $keys[$mode][$i][5]]) << 8)
771
+ | (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $keys[$mode][$i][6]]) << 4)
772
+ | ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $keys[$mode][$i][7]]);
773
+
774
+ $msb = ($temp >> 31) & 1;
775
+ $temp &= 0x7FFFFFFF;
776
+ $newBlock = (($temp & 0x00010000) << 15) | (($temp & 0x02020120) << 5)
777
+ | (($temp & 0x00001800) << 17) | (($temp & 0x01000000) >> 10)
778
+ | (($temp & 0x00000008) << 24) | (($temp & 0x00100000) << 6)
779
+ | (($temp & 0x00000010) << 21) | (($temp & 0x00008000) << 9)
780
+ | (($temp & 0x00000200) << 12) | (($temp & 0x10000000) >> 27)
781
+ | (($temp & 0x00000040) << 14) | (($temp & 0x08000000) >> 8)
782
+ | (($temp & 0x00004000) << 4) | (($temp & 0x00000002) << 16)
783
+ | (($temp & 0x00442000) >> 6) | (($temp & 0x40800000) >> 15)
784
+ | (($temp & 0x00000001) << 11) | (($temp & 0x20000000) >> 20)
785
+ | (($temp & 0x00080000) >> 13) | (($temp & 0x00000004) << 3)
786
+ | (($temp & 0x04000000) >> 22) | (($temp & 0x00000480) >> 7)
787
+ | (($temp & 0x00200000) >> 19) | ($msb << 23);
788
+ // end of "the Feistel (F) function" - $newBlock is F's output
789
+
790
+ $temp = $block[1];
791
+ $block[1] = $block[0] ^ $newBlock;
792
+ $block[0] = $temp;
793
+ }
794
+
795
+ $msb = array(
796
+ ($block[0] >> 31) & 1,
797
+ ($block[1] >> 31) & 1
798
+ );
799
+ $block[0] &= 0x7FFFFFFF;
800
+ $block[1] &= 0x7FFFFFFF;
801
+
802
+ $block = array(
803
+ (($block[0] & 0x01000004) << 7) | (($block[1] & 0x01000004) << 6) |
804
+ (($block[0] & 0x00010000) << 13) | (($block[1] & 0x00010000) << 12) |
805
+ (($block[0] & 0x00000100) << 19) | (($block[1] & 0x00000100) << 18) |
806
+ (($block[0] & 0x00000001) << 25) | (($block[1] & 0x00000001) << 24) |
807
+ (($block[0] & 0x02000008) >> 2) | (($block[1] & 0x02000008) >> 3) |
808
+ (($block[0] & 0x00020000) << 4) | (($block[1] & 0x00020000) << 3) |
809
+ (($block[0] & 0x00000200) << 10) | (($block[1] & 0x00000200) << 9) |
810
+ (($block[0] & 0x00000002) << 16) | (($block[1] & 0x00000002) << 15) |
811
+ (($block[0] & 0x04000000) >> 11) | (($block[1] & 0x04000000) >> 12) |
812
+ (($block[0] & 0x00040000) >> 5) | (($block[1] & 0x00040000) >> 6) |
813
+ (($block[0] & 0x00000400) << 1) | ( $block[1] & 0x00000400 ) |
814
+ (($block[0] & 0x08000000) >> 20) | (($block[1] & 0x08000000) >> 21) |
815
+ (($block[0] & 0x00080000) >> 14) | (($block[1] & 0x00080000) >> 15) |
816
+ (($block[0] & 0x00000800) >> 8) | (($block[1] & 0x00000800) >> 9)
817
+ ,
818
+ (($block[0] & 0x10000040) << 3) | (($block[1] & 0x10000040) << 2) |
819
+ (($block[0] & 0x00100000) << 9) | (($block[1] & 0x00100000) << 8) |
820
+ (($block[0] & 0x00001000) << 15) | (($block[1] & 0x00001000) << 14) |
821
+ (($block[0] & 0x00000010) << 21) | (($block[1] & 0x00000010) << 20) |
822
+ (($block[0] & 0x20000080) >> 6) | (($block[1] & 0x20000080) >> 7) |
823
+ ( $block[0] & 0x00200000 ) | (($block[1] & 0x00200000) >> 1) |
824
+ (($block[0] & 0x00002000) << 6) | (($block[1] & 0x00002000) << 5) |
825
+ (($block[0] & 0x00000020) << 12) | (($block[1] & 0x00000020) << 11) |
826
+ (($block[0] & 0x40000000) >> 15) | (($block[1] & 0x40000000) >> 16) |
827
+ (($block[0] & 0x00400000) >> 9) | (($block[1] & 0x00400000) >> 10) |
828
+ (($block[0] & 0x00004000) >> 3) | (($block[1] & 0x00004000) >> 4) |
829
+ (($block[0] & 0x00800000) >> 18) | (($block[1] & 0x00800000) >> 19) |
830
+ (($block[0] & 0x00008000) >> 12) | (($block[1] & 0x00008000) >> 13) |
831
+ ($msb[0] << 7) | ($msb[1] << 6)
832
+ );
833
+
834
+ return pack('NN', $block[0], $block[1]);
835
+ }
836
+
837
+ /**
838
+ * Creates the key schedule.
839
+ *
840
+ * @access private
841
+ * @param String $key
842
+ * @return Array
843
+ */
844
+ function _prepareKey($key)
845
+ {
846
+ static $shifts = array( // number of key bits shifted per round
847
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
848
+ );
849
+
850
+ // pad the key and remove extra characters as appropriate.
851
+ $key = str_pad(substr($key, 0, 8), 8, chr(0));
852
+
853
+ $temp = unpack('Na/Nb', $key);
854
+ $key = array($temp['a'], $temp['b']);
855
+ $msb = array(
856
+ ($key[0] >> 31) & 1,
857
+ ($key[1] >> 31) & 1
858
+ );
859
+ $key[0] &= 0x7FFFFFFF;
860
+ $key[1] &= 0x7FFFFFFF;
861
+
862
+ $key = array(
863
+ (($key[1] & 0x00000002) << 26) | (($key[1] & 0x00000204) << 17) |
864
+ (($key[1] & 0x00020408) << 8) | (($key[1] & 0x02040800) >> 1) |
865
+ (($key[0] & 0x00000002) << 22) | (($key[0] & 0x00000204) << 13) |
866
+ (($key[0] & 0x00020408) << 4) | (($key[0] & 0x02040800) >> 5) |
867
+ (($key[1] & 0x04080000) >> 10) | (($key[0] & 0x04080000) >> 14) |
868
+ (($key[1] & 0x08000000) >> 19) | (($key[0] & 0x08000000) >> 23) |
869
+ (($key[0] & 0x00000010) >> 1) | (($key[0] & 0x00001000) >> 10) |
870
+ (($key[0] & 0x00100000) >> 19) | (($key[0] & 0x10000000) >> 28)
871
+ ,
872
+ (($key[1] & 0x00000080) << 20) | (($key[1] & 0x00008000) << 11) |
873
+ (($key[1] & 0x00800000) << 2) | (($key[0] & 0x00000080) << 16) |
874
+ (($key[0] & 0x00008000) << 7) | (($key[0] & 0x00800000) >> 2) |
875
+ (($key[1] & 0x00000040) << 13) | (($key[1] & 0x00004000) << 4) |
876
+ (($key[1] & 0x00400000) >> 5) | (($key[1] & 0x40000000) >> 14) |
877
+ (($key[0] & 0x00000040) << 9) | ( $key[0] & 0x00004000 ) |
878
+ (($key[0] & 0x00400000) >> 9) | (($key[0] & 0x40000000) >> 18) |
879
+ (($key[1] & 0x00000020) << 6) | (($key[1] & 0x00002000) >> 3) |
880
+ (($key[1] & 0x00200000) >> 12) | (($key[1] & 0x20000000) >> 21) |
881
+ (($key[0] & 0x00000020) << 2) | (($key[0] & 0x00002000) >> 7) |
882
+ (($key[0] & 0x00200000) >> 16) | (($key[0] & 0x20000000) >> 25) |
883
+ (($key[1] & 0x00000010) >> 1) | (($key[1] & 0x00001000) >> 10) |
884
+ (($key[1] & 0x00100000) >> 19) | (($key[1] & 0x10000000) >> 28) |
885
+ ($msb[1] << 24) | ($msb[0] << 20)
886
+ );
887
+
888
+ $keys = array();
889
+ for ($i = 0; $i < 16; $i++) {
890
+ $key[0] <<= $shifts[$i];
891
+ $temp = ($key[0] & 0xF0000000) >> 28;
892
+ $key[0] = ($key[0] | $temp) & 0x0FFFFFFF;
893
+
894
+ $key[1] <<= $shifts[$i];
895
+ $temp = ($key[1] & 0xF0000000) >> 28;
896
+ $key[1] = ($key[1] | $temp) & 0x0FFFFFFF;
897
+
898
+ $temp = array(
899
+ (($key[1] & 0x00004000) >> 9) | (($key[1] & 0x00000800) >> 7) |
900
+ (($key[1] & 0x00020000) >> 14) | (($key[1] & 0x00000010) >> 2) |
901
+ (($key[1] & 0x08000000) >> 26) | (($key[1] & 0x00800000) >> 23)
902
+ ,
903
+ (($key[1] & 0x02400000) >> 20) | (($key[1] & 0x00000001) << 4) |
904
+ (($key[1] & 0x00002000) >> 10) | (($key[1] & 0x00040000) >> 18) |
905
+ (($key[1] & 0x00000080) >> 6)
906
+ ,
907
+ ( $key[1] & 0x00000020 ) | (($key[1] & 0x00000200) >> 5) |
908
+ (($key[1] & 0x00010000) >> 13) | (($key[1] & 0x01000000) >> 22) |
909
+ (($key[1] & 0x00000004) >> 1) | (($key[1] & 0x00100000) >> 20)
910
+ ,
911
+ (($key[1] & 0x00001000) >> 7) | (($key[1] & 0x00200000) >> 17) |
912
+ (($key[1] & 0x00000002) << 2) | (($key[1] & 0x00000100) >> 6) |
913
+ (($key[1] & 0x00008000) >> 14) | (($key[1] & 0x04000000) >> 26)
914
+ ,
915
+ (($key[0] & 0x00008000) >> 10) | ( $key[0] & 0x00000010 ) |
916
+ (($key[0] & 0x02000000) >> 22) | (($key[0] & 0x00080000) >> 17) |
917
+ (($key[0] & 0x00000200) >> 8) | (($key[0] & 0x00000002) >> 1)
918
+ ,
919
+ (($key[0] & 0x04000000) >> 21) | (($key[0] & 0x00010000) >> 12) |
920
+ (($key[0] & 0x00000020) >> 2) | (($key[0] & 0x00000800) >> 9) |
921
+ (($key[0] & 0x00800000) >> 22) | (($key[0] & 0x00000100) >> 8)
922
+ ,
923
+ (($key[0] & 0x00001000) >> 7) | (($key[0] & 0x00000088) >> 3) |
924
+ (($key[0] & 0x00020000) >> 14) | (($key[0] & 0x00000001) << 2) |
925
+ (($key[0] & 0x00400000) >> 21)
926
+ ,
927
+ (($key[0] & 0x00000400) >> 5) | (($key[0] & 0x00004000) >> 10) |
928
+ (($key[0] & 0x00000040) >> 3) | (($key[0] & 0x00100000) >> 18) |
929
+ (($key[0] & 0x08000000) >> 26) | (($key[0] & 0x01000000) >> 24)
930
+ );
931
+
932
+ $keys[] = $temp;
933
+ }
934
+
935
+ $temp = array(
936
+ CRYPT_DES_ENCRYPT => $keys,
937
+ CRYPT_DES_DECRYPT => array_reverse($keys)
938
+ );
939
+
940
+ return $temp;
941
+ }
942
+ }
943
+
944
+ // vim: ts=4:sw=4:et:
945
+ // vim6: fdl=1:
app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/Hash.php ADDED
@@ -0,0 +1,816 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
6
+ *
7
+ * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following:
8
+ *
9
+ * md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512
10
+ *
11
+ * If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as opposed to
12
+ * the hash. If no valid algorithm is provided, sha1 will be used.
13
+ *
14
+ * PHP versions 4 and 5
15
+ *
16
+ * {@internal The variable names are the same as those in
17
+ * {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
18
+ *
19
+ * Here's a short example of how to use this library:
20
+ * <code>
21
+ * <?php
22
+ * include('Crypt/Hash.php');
23
+ *
24
+ * $hash = new Crypt_Hash('sha1');
25
+ *
26
+ * $hash->setKey('abcdefg');
27
+ *
28
+ * echo base64_encode($hash->hash('abcdefg'));
29
+ * ?>
30
+ * </code>
31
+ *
32
+ * LICENSE: This library is free software; you can redistribute it and/or
33
+ * modify it under the terms of the GNU Lesser General Public
34
+ * License as published by the Free Software Foundation; either
35
+ * version 2.1 of the License, or (at your option) any later version.
36
+ *
37
+ * This library is distributed in the hope that it will be useful,
38
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
39
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
40
+ * Lesser General Public License for more details.
41
+ *
42
+ * You should have received a copy of the GNU Lesser General Public
43
+ * License along with this library; if not, write to the Free Software
44
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
45
+ * MA 02111-1307 USA
46
+ *
47
+ * @category Crypt
48
+ * @package Crypt_Hash
49
+ * @author Jim Wigginton <terrafrost@php.net>
50
+ * @copyright MMVII Jim Wigginton
51
+ * @license http://www.gnu.org/licenses/lgpl.txt
52
+ * @version $Id: Hash.php,v 1.6 2009/11/23 23:37:07 terrafrost Exp $
53
+ * @link http://phpseclib.sourceforge.net
54
+ */
55
+
56
+ /**#@+
57
+ * @access private
58
+ * @see Crypt_Hash::Crypt_Hash()
59
+ */
60
+ /**
61
+ * Toggles the internal implementation
62
+ */
63
+ define('CRYPT_HASH_MODE_INTERNAL', 1);
64
+ /**
65
+ * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+.
66
+ */
67
+ define('CRYPT_HASH_MODE_MHASH', 2);
68
+ /**
69
+ * Toggles the hash() implementation, which works on PHP 5.1.2+.
70
+ */
71
+ define('CRYPT_HASH_MODE_HASH', 3);
72
+ /**#@-*/
73
+
74
+ /**
75
+ * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
76
+ *
77
+ * @author Jim Wigginton <terrafrost@php.net>
78
+ * @version 0.1.0
79
+ * @access public
80
+ * @package Crypt_Hash
81
+ */
82
+ class Crypt_Hash {
83
+ /**
84
+ * Byte-length of compression blocks / key (Internal HMAC)
85
+ *
86
+ * @see Crypt_Hash::setAlgorithm()
87
+ * @var Integer
88
+ * @access private
89
+ */
90
+ var $b;
91
+
92
+ /**
93
+ * Byte-length of hash output (Internal HMAC)
94
+ *
95
+ * @see Crypt_Hash::setHash()
96
+ * @var Integer
97
+ * @access private
98
+ */
99
+ var $l = false;
100
+
101
+ /**
102
+ * Hash Algorithm
103
+ *
104
+ * @see Crypt_Hash::setHash()
105
+ * @var String
106
+ * @access private
107
+ */
108
+ var $hash;
109
+
110
+ /**
111
+ * Key
112
+ *
113
+ * @see Crypt_Hash::setKey()
114
+ * @var String
115
+ * @access private
116
+ */
117
+ var $key = '';
118
+
119
+ /**
120
+ * Outer XOR (Internal HMAC)
121
+ *
122
+ * @see Crypt_Hash::setKey()
123
+ * @var String
124
+ * @access private
125
+ */
126
+ var $opad;
127
+
128
+ /**
129
+ * Inner XOR (Internal HMAC)
130
+ *
131
+ * @see Crypt_Hash::setKey()
132
+ * @var String
133
+ * @access private
134
+ */
135
+ var $ipad;
136
+
137
+ /**
138
+ * Default Constructor.
139
+ *
140
+ * @param optional String $hash
141
+ * @return Crypt_Hash
142
+ * @access public
143
+ */
144
+ function Crypt_Hash($hash = 'sha1')
145
+ {
146
+ if ( !defined('CRYPT_HASH_MODE') ) {
147
+ switch (true) {
148
+ case extension_loaded('hash'):
149
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH);
150
+ break;
151
+ case extension_loaded('mhash'):
152
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH);
153
+ break;
154
+ default:
155
+ define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL);
156
+ }
157
+ }
158
+
159
+ $this->setHash($hash);
160
+ }
161
+
162
+ /**
163
+ * Sets the key for HMACs
164
+ *
165
+ * Keys can be of any length.
166
+ *
167
+ * @access public
168
+ * @param String $key
169
+ */
170
+ function setKey($key)
171
+ {
172
+ $this->key = $key;
173
+ }
174
+
175
+ /**
176
+ * Sets the hash function.
177
+ *
178
+ * @access public
179
+ * @param String $hash
180
+ */
181
+ function setHash($hash)
182
+ {
183
+ switch ($hash) {
184
+ case 'md5-96':
185
+ case 'sha1-96':
186
+ $this->l = 12; // 96 / 8 = 12
187
+ break;
188
+ case 'md2':
189
+ case 'md5':
190
+ $this->l = 16;
191
+ break;
192
+ case 'sha1':
193
+ $this->l = 20;
194
+ break;
195
+ case 'sha256':
196
+ $this->l = 32;
197
+ break;
198
+ case 'sha384':
199
+ $this->l = 48;
200
+ break;
201
+ case 'sha512':
202
+ $this->l = 64;
203
+ }
204
+
205
+ switch ($hash) {
206
+ case 'md2':
207
+ $mode = CRYPT_HASH_MODE_INTERNAL;
208
+ break;
209
+ case 'sha384':
210
+ case 'sha512':
211
+ $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
212
+ break;
213
+ default:
214
+ $mode = CRYPT_HASH_MODE;
215
+ }
216
+
217
+ switch ( $mode ) {
218
+ case CRYPT_HASH_MODE_MHASH:
219
+ switch ($hash) {
220
+ case 'md5':
221
+ case 'md5-96':
222
+ $this->hash = MHASH_MD5;
223
+ break;
224
+ case 'sha256':
225
+ $this->hash = MHASH_SHA256;
226
+ break;
227
+ case 'sha1':
228
+ case 'sha1-96':
229
+ default:
230
+ $this->hash = MHASH_SHA1;
231
+ }
232
+ return;
233
+ case CRYPT_HASH_MODE_HASH:
234
+ switch ($hash) {
235
+ case 'md5':
236
+ case 'md5-96':
237
+ $this->hash = 'md5';
238
+ return;
239
+ case 'sha256':
240
+ case 'sha384':
241
+ case 'sha512':
242
+ $this->hash = $hash;
243
+ return;
244
+ case 'sha1':
245
+ case 'sha1-96':
246
+ default:
247
+ $this->hash = 'sha1';
248
+ }
249
+ return;
250
+ }
251
+
252
+ switch ($hash) {
253
+ case 'md2':
254
+ $this->b = 16;
255
+ $this->hash = array($this, '_md2');
256
+ break;
257
+ case 'md5':
258
+ case 'md5-96':
259
+ $this->b = 64;
260
+ $this->hash = array($this, '_md5');
261
+ break;
262
+ case 'sha256':
263
+ $this->b = 64;
264
+ $this->hash = array($this, '_sha256');
265
+ break;
266
+ case 'sha384':
267
+ case 'sha512':
268
+ $this->b = 128;
269
+ $this->hash = array($this, '_sha512');
270
+ break;
271
+ case 'sha1':
272
+ case 'sha1-96':
273
+ default:
274
+ $this->b = 64;
275
+ $this->hash = array($this, '_sha1');
276
+ }
277
+
278
+ $this->ipad = str_repeat(chr(0x36), $this->b);
279
+ $this->opad = str_repeat(chr(0x5C), $this->b);
280
+ }
281
+
282
+ /**
283
+ * Compute the HMAC.
284
+ *
285
+ * @access public
286
+ * @param String $text
287
+ * @return String
288
+ */
289
+ function hash($text)
290
+ {
291
+ $mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
292
+
293
+ if (!empty($this->key)) {
294
+ switch ( $mode ) {
295
+ case CRYPT_HASH_MODE_MHASH:
296
+ $output = mhash($this->hash, $text, $this->key);
297
+ break;
298
+ case CRYPT_HASH_MODE_HASH:
299
+ $output = hash_hmac($this->hash, $text, $this->key, true);
300
+ break;
301
+ case CRYPT_HASH_MODE_INTERNAL:
302
+ /* "Applications that use keys longer than B bytes will first hash the key using H and then use the
303
+ resultant L byte string as the actual key to HMAC."
304
+
305
+ -- http://tools.ietf.org/html/rfc2104#section-2 */
306
+ $key = strlen($this->key) > $this->b ? call_user_func($this->$hash, $this->key) : $this->key;
307
+
308
+ $key = str_pad($key, $this->b, chr(0)); // step 1
309
+ $temp = $this->ipad ^ $key; // step 2
310
+ $temp .= $text; // step 3
311
+ $temp = call_user_func($this->hash, $temp); // step 4
312
+ $output = $this->opad ^ $key; // step 5
313
+ $output.= $temp; // step 6
314
+ $output = call_user_func($this->hash, $output); // step 7
315
+ }
316
+ } else {
317
+ switch ( $mode ) {
318
+ case CRYPT_HASH_MODE_MHASH:
319
+ $output = mhash($this->hash, $text);
320
+ break;
321
+ case CRYPT_HASH_MODE_HASH:
322
+ $output = hash($this->hash, $text, true);
323
+ break;
324
+ case CRYPT_HASH_MODE_INTERNAL:
325
+ $output = call_user_func($this->hash, $text);
326
+ }
327
+ }
328
+
329
+ return substr($output, 0, $this->l);
330
+ }
331
+
332
+ /**
333
+ * Returns the hash length (in bytes)
334
+ *
335
+ * @access private
336
+ * @return Integer
337
+ */
338
+ function getLength()
339
+ {
340
+ return $this->l;
341
+ }
342
+
343
+ /**
344
+ * Wrapper for MD5
345
+ *
346
+ * @access private
347
+ * @param String $text
348
+ */
349
+ function _md5($m)
350
+ {
351
+ return pack('H*', md5($m));
352
+ }
353
+
354
+ /**
355
+ * Wrapper for SHA1
356
+ *
357
+ * @access private
358
+ * @param String $text
359
+ */
360
+ function _sha1($m)
361
+ {
362
+ return pack('H*', sha1($m));
363
+ }
364
+
365
+ /**
366
+ * Pure-PHP implementation of MD2
367
+ *
368
+ * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
369
+ *
370
+ * @access private
371
+ * @param String $text
372
+ */
373
+ function _md2($m)
374
+ {
375
+ static $s = array(
376
+ 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
377
+ 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
378
+ 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
379
+ 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
380
+ 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
381
+ 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
382
+ 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
383
+ 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
384
+ 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
385
+ 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
386
+ 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
387
+ 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
388
+ 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
389
+ 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
390
+ 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
391
+ 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
392
+ 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
393
+ 31, 26, 219, 153, 141, 51, 159, 17, 131, 20
394
+ );
395
+
396
+ // Step 1. Append Padding Bytes
397
+ $pad = 16 - (strlen($m) & 0xF);
398
+ $m.= str_repeat(chr($pad), $pad);
399
+
400
+ $length = strlen($m);
401
+
402
+ // Step 2. Append Checksum
403
+ $c = str_repeat(chr(0), 16);
404
+ $l = chr(0);
405
+ for ($i = 0; $i < $length; $i+= 16) {
406
+ for ($j = 0; $j < 16; $j++) {
407
+ $c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
408
+ $l = $c[$j];
409
+ }
410
+ }
411
+ $m.= $c;
412
+
413
+ $length+= 16;
414
+
415
+ // Step 3. Initialize MD Buffer
416
+ $x = str_repeat(chr(0), 48);
417
+
418
+ // Step 4. Process Message in 16-Byte Blocks
419
+ for ($i = 0; $i < $length; $i+= 16) {
420
+ for ($j = 0; $j < 16; $j++) {
421
+ $x[$j + 16] = $m[$i + $j];
422
+ $x[$j + 32] = $x[$j + 16] ^ $x[$j];
423
+ }
424
+ $t = chr(0);
425
+ for ($j = 0; $j < 18; $j++) {
426
+ for ($k = 0; $k < 48; $k++) {
427
+ $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
428
+ //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
429
+ }
430
+ $t = chr(ord($t) + $j);
431
+ }
432
+ }
433
+
434
+ // Step 5. Output
435
+ return substr($x, 0, 16);
436
+ }
437
+
438
+ /**
439
+ * Pure-PHP implementation of SHA256
440
+ *
441
+ * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
442
+ *
443
+ * @access private
444
+ * @param String $text
445
+ */
446
+ function _sha256($m)
447
+ {
448
+ if (extension_loaded('suhosin')) {
449
+ return pack('H*', sha256($m));
450
+ }
451
+
452
+ // Initialize variables
453
+ $hash = array(
454
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
455
+ );
456
+ // Initialize table of round constants
457
+ // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
458
+ static $k = array(
459
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
460
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
461
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
462
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
463
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
464
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
465
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
466
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
467
+ );
468
+
469
+ // Pre-processing
470
+ $length = strlen($m);
471
+ // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64
472
+ $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
473
+ $m[$length] = chr(0x80);
474
+ // we don't support hashing strings 512MB long
475
+ $m.= pack('N2', 0, $length << 3);
476
+
477
+ // Process the message in successive 512-bit chunks
478
+ $chunks = str_split($m, 64);
479
+ foreach ($chunks as $chunk) {
480
+ $w = array();
481
+ for ($i = 0; $i < 16; $i++) {
482
+ extract(unpack('Ntemp', $this->_string_shift($chunk, 4)));
483
+ $w[] = $temp;
484
+ }
485
+
486
+ // Extend the sixteen 32-bit words into sixty-four 32-bit words
487
+ for ($i = 16; $i < 64; $i++) {
488
+ $s0 = $this->_rightRotate($w[$i - 15], 7) ^
489
+ $this->_rightRotate($w[$i - 15], 18) ^
490
+ $this->_rightShift( $w[$i - 15], 3);
491
+ $s1 = $this->_rightRotate($w[$i - 2], 17) ^
492
+ $this->_rightRotate($w[$i - 2], 19) ^
493
+ $this->_rightShift( $w[$i - 2], 10);
494
+ $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
495
+
496
+ }
497
+
498
+ // Initialize hash value for this chunk
499
+ list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
500
+
501
+ // Main loop
502
+ for ($i = 0; $i < 64; $i++) {
503
+ $s0 = $this->_rightRotate($a, 2) ^
504
+ $this->_rightRotate($a, 13) ^
505
+ $this->_rightRotate($a, 22);
506
+ $maj = ($a & $b) ^
507
+ ($a & $c) ^
508
+ ($b & $c);
509
+ $t2 = $this->_add($s0, $maj);
510
+
511
+ $s1 = $this->_rightRotate($e, 6) ^
512
+ $this->_rightRotate($e, 11) ^
513
+ $this->_rightRotate($e, 25);
514
+ $ch = ($e & $f) ^
515
+ ($this->_not($e) & $g);
516
+ $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);
517
+
518
+ $h = $g;
519
+ $g = $f;
520
+ $f = $e;
521
+ $e = $this->_add($d, $t1);
522
+ $d = $c;
523
+ $c = $b;
524
+ $b = $a;
525
+ $a = $this->_add($t1, $t2);
526
+ }
527
+
528
+ // Add this chunk's hash to result so far
529
+ $hash = array(
530
+ $this->_add($hash[0], $a),
531
+ $this->_add($hash[1], $b),
532
+ $this->_add($hash[2], $c),
533
+ $this->_add($hash[3], $d),
534
+ $this->_add($hash[4], $e),
535
+ $this->_add($hash[5], $f),
536
+ $this->_add($hash[6], $g),
537
+ $this->_add($hash[7], $h)
538
+ );
539
+ }
540
+
541
+ // Produce the final hash value (big-endian)
542
+ return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]);
543
+ }
544
+
545
+ /**
546
+ * Pure-PHP implementation of SHA384 and SHA512
547
+ *
548
+ * @access private
549
+ * @param String $text
550
+ */
551
+ function _sha512($m)
552
+ {
553
+ if (!class_exists('Math_BigInteger')) {
554
+ require_once('Math/BigInteger.php');
555
+ }
556
+
557
+ static $init384, $init512, $k;
558
+
559
+ if (!isset($k)) {
560
+ // Initialize variables
561
+ $init384 = array( // initial values for SHA384
562
+ 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
563
+ '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
564
+ );
565
+ $init512 = array( // initial values for SHA512
566
+ '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
567
+ '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
568
+ );
569
+
570
+ for ($i = 0; $i < 8; $i++) {
571
+ $init384[$i] = new Math_BigInteger($init384[$i], 16);
572
+ $init384[$i]->setPrecision(64);
573
+ $init512[$i] = new Math_BigInteger($init512[$i], 16);
574
+ $init512[$i]->setPrecision(64);
575
+ }
576
+
577
+ // Initialize table of round constants
578
+ // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
579
+ $k = array(
580
+ '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
581
+ '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
582
+ 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
583
+ '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
584
+ 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
585
+ '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
586
+ '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
587
+ 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
588
+ '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
589
+ '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
590
+ 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
591
+ 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
592
+ '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
593
+ '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
594
+ '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
595
+ '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
596
+ 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
597
+ '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
598
+ '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
599
+ '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
600
+ );
601
+
602
+ for ($i = 0; $i < 80; $i++) {
603
+ $k[$i] = new Math_BigInteger($k[$i], 16);
604
+ }
605
+ }
606
+
607
+ $hash = $this->l == 48 ? $init384 : $init512;
608
+
609
+ // Pre-processing
610
+ $length = strlen($m);
611
+ // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
612
+ $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
613
+ $m[$length] = chr(0x80);
614
+ // we don't support hashing strings 512MB long
615
+ $m.= pack('N4', 0, 0, 0, $length << 3);
616
+
617
+ // Process the message in successive 1024-bit chunks
618
+ $chunks = str_split($m, 128);
619
+ foreach ($chunks as $chunk) {
620
+ $w = array();
621
+ for ($i = 0; $i < 16; $i++) {
622
+ $temp = new Math_BigInteger($this->_string_shift($chunk, 8), 256);
623
+ $temp->setPrecision(64);
624
+ $w[] = $temp;
625
+ }
626
+
627
+ // Extend the sixteen 32-bit words into eighty 32-bit words
628
+ for ($i = 16; $i < 80; $i++) {
629
+ $temp = array(
630
+ $w[$i - 15]->bitwise_rightRotate(1),
631
+ $w[$i - 15]->bitwise_rightRotate(8),
632
+ $w[$i - 15]->bitwise_rightShift(7)
633
+ );
634
+ $s0 = $temp[0]->bitwise_xor($temp[1]);
635
+ $s0 = $s0->bitwise_xor($temp[2]);
636
+ $temp = array(
637
+ $w[$i - 2]->bitwise_rightRotate(19),
638
+ $w[$i - 2]->bitwise_rightRotate(61),
639
+ $w[$i - 2]->bitwise_rightShift(6)
640
+ );
641
+ $s1 = $temp[0]->bitwise_xor($temp[1]);
642
+ $s1 = $s1->bitwise_xor($temp[2]);
643
+ $w[$i] = $w[$i - 16]->copy();
644
+ $w[$i] = $w[$i]->add($s0);
645
+ $w[$i] = $w[$i]->add($w[$i - 7]);
646
+ $w[$i] = $w[$i]->add($s1);
647
+ }
648
+
649
+ // Initialize hash value for this chunk
650
+ $a = $hash[0]->copy();
651
+ $b = $hash[1]->copy();
652
+ $c = $hash[2]->copy();
653
+ $d = $hash[3]->copy();
654
+ $e = $hash[4]->copy();
655
+ $f = $hash[5]->copy();
656
+ $g = $hash[6]->copy();
657
+ $h = $hash[7]->copy();
658
+
659
+ // Main loop
660
+ for ($i = 0; $i < 80; $i++) {
661
+ $temp = array(
662
+ $a->bitwise_rightRotate(28),
663
+ $a->bitwise_rightRotate(34),
664
+ $a->bitwise_rightRotate(39)
665
+ );
666
+ $s0 = $temp[0]->bitwise_xor($temp[1]);
667
+ $s0 = $s0->bitwise_xor($temp[2]);
668
+ $temp = array(
669
+ $a->bitwise_and($b),
670
+ $a->bitwise_and($c),
671
+ $b->bitwise_and($c)
672
+ );
673
+ $maj = $temp[0]->bitwise_xor($temp[1]);
674
+ $maj = $maj->bitwise_xor($temp[2]);
675
+ $t2 = $s0->add($maj);
676
+
677
+ $temp = array(
678
+ $e->bitwise_rightRotate(14),
679
+ $e->bitwise_rightRotate(18),
680
+ $e->bitwise_rightRotate(41)
681
+ );
682
+ $s1 = $temp[0]->bitwise_xor($temp[1]);
683
+ $s1 = $s1->bitwise_xor($temp[2]);
684
+ $temp = array(
685
+ $e->bitwise_and($f),
686
+ $g->bitwise_and($e->bitwise_not())
687
+ );
688
+ $ch = $temp[0]->bitwise_xor($temp[1]);
689
+ $t1 = $h->add($s1);
690
+ $t1 = $t1->add($ch);
691
+ $t1 = $t1->add($k[$i]);
692
+ $t1 = $t1->add($w[$i]);
693
+
694
+ $h = $g->copy();
695
+ $g = $f->copy();
696
+ $f = $e->copy();
697
+ $e = $d->add($t1);
698
+ $d = $c->copy();
699
+ $c = $b->copy();
700
+ $b = $a->copy();
701
+ $a = $t1->add($t2);
702
+ }
703
+
704
+ // Add this chunk's hash to result so far
705
+ $hash = array(
706
+ $hash[0]->add($a),
707
+ $hash[1]->add($b),
708
+ $hash[2]->add($c),
709
+ $hash[3]->add($d),
710
+ $hash[4]->add($e),
711
+ $hash[5]->add($f),
712
+ $hash[6]->add($g),
713
+ $hash[7]->add($h)
714
+ );
715
+ }
716
+
717
+ // Produce the final hash value (big-endian)
718
+ // (Crypt_Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
719
+ $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
720
+ $hash[4]->toBytes() . $hash[5]->toBytes();
721
+ if ($this->l != 48) {
722
+ $temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
723
+ }
724
+
725
+ return $temp;
726
+ }
727
+
728
+ /**
729
+ * Right Rotate
730
+ *
731
+ * @access private
732
+ * @param Integer $int
733
+ * @param Integer $amt
734
+ * @see _sha256()
735
+ * @return Integer
736
+ */
737
+ function _rightRotate($int, $amt)
738
+ {
739
+ $invamt = 32 - $amt;
740
+ $mask = (1 << $invamt) - 1;
741
+ return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask);
742
+ }
743
+
744
+ /**
745
+ * Right Shift
746
+ *
747
+ * @access private
748
+ * @param Integer $int
749
+ * @param Integer $amt
750
+ * @see _sha256()
751
+ * @return Integer
752
+ */
753
+ function _rightShift($int, $amt)
754
+ {
755
+ $mask = (1 << (32 - $amt)) - 1;
756
+ return ($int >> $amt) & $mask;
757
+ }
758
+
759
+ /**
760
+ * Not
761
+ *
762
+ * @access private
763
+ * @param Integer $int
764
+ * @see _sha256()
765
+ * @return Integer
766
+ */
767
+ function _not($int)
768
+ {
769
+ return ~$int & 0xFFFFFFFF;
770
+ }
771
+
772
+ /**
773
+ * Add
774
+ *
775
+ * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the
776
+ * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster.
777
+ *
778
+ * @param String $string
779
+ * @param optional Integer $index
780
+ * @return String
781
+ * @see _sha256()
782
+ * @access private
783
+ */
784
+ function _add()
785
+ {
786
+ static $mod;
787
+ if (!isset($mod)) {
788
+ $mod = pow(2, 32);
789
+ }
790
+
791
+ $result = 0;
792
+ $arguments = func_get_args();
793
+ foreach ($arguments as $argument) {
794
+ $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
795
+ }
796
+
797
+ return fmod($result, $mod);
798
+ }
799
+
800
+ /**
801
+ * String Shift
802
+ *
803
+ * Inspired by array_shift
804
+ *
805
+ * @param String $string
806
+ * @param optional Integer $index
807
+ * @return String
808
+ * @access private
809
+ */
810
+ function _string_shift(&$string, $index = 1)
811
+ {
812
+ $substr = substr($string, 0, $index);
813
+ $string = substr($string, $index);
814
+ return $substr;
815
+ }
816
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/RC4.php ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of RC4.
6
+ *
7
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * Useful resources are as follows:
12
+ *
13
+ * - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
14
+ * - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
15
+ *
16
+ * RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not
17
+ * ARCFOUR or ARC4 because RC4 is how it is refered to in the SSH1 specification.
18
+ *
19
+ * Here's a short example of how to use this library:
20
+ * <code>
21
+ * <?php
22
+ * include('Crypt/RC4.php');
23
+ *
24
+ * $rc4 = new Crypt_RC4();
25
+ *
26
+ * $rc4->setKey('abcdefgh');
27
+ *
28
+ * $size = 10 * 1024;
29
+ * $plaintext = '';
30
+ * for ($i = 0; $i < $size; $i++) {
31
+ * $plaintext.= 'a';
32
+ * }
33
+ *
34
+ * echo $rc4->decrypt($rc4->encrypt($plaintext));
35
+ * ?>
36
+ * </code>
37
+ *
38
+ * LICENSE: This library is free software; you can redistribute it and/or
39
+ * modify it under the terms of the GNU Lesser General Public
40
+ * License as published by the Free Software Foundation; either
41
+ * version 2.1 of the License, or (at your option) any later version.
42
+ *
43
+ * This library is distributed in the hope that it will be useful,
44
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
45
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46
+ * Lesser General Public License for more details.
47
+ *
48
+ * You should have received a copy of the GNU Lesser General Public
49
+ * License along with this library; if not, write to the Free Software
50
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
51
+ * MA 02111-1307 USA
52
+ *
53
+ * @category Crypt
54
+ * @package Crypt_RC4
55
+ * @author Jim Wigginton <terrafrost@php.net>
56
+ * @copyright MMVII Jim Wigginton
57
+ * @license http://www.gnu.org/licenses/lgpl.txt
58
+ * @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $
59
+ * @link http://phpseclib.sourceforge.net
60
+ */
61
+
62
+ /**#@+
63
+ * @access private
64
+ * @see Crypt_RC4::Crypt_RC4()
65
+ */
66
+ /**
67
+ * Toggles the internal implementation
68
+ */
69
+ define('CRYPT_RC4_MODE_INTERNAL', 1);
70
+ /**
71
+ * Toggles the mcrypt implementation
72
+ */
73
+ define('CRYPT_RC4_MODE_MCRYPT', 2);
74
+ /**#@-*/
75
+
76
+ /**#@+
77
+ * @access private
78
+ * @see Crypt_RC4::_crypt()
79
+ */
80
+ define('CRYPT_RC4_ENCRYPT', 0);
81
+ define('CRYPT_RC4_DECRYPT', 1);
82
+ /**#@-*/
83
+
84
+ /**
85
+ * Pure-PHP implementation of RC4.
86
+ *
87
+ * @author Jim Wigginton <terrafrost@php.net>
88
+ * @version 0.1.0
89
+ * @access public
90
+ * @package Crypt_RC4
91
+ */
92
+ class Crypt_RC4 {
93
+ /**
94
+ * The Key
95
+ *
96
+ * @see Crypt_RC4::setKey()
97
+ * @var String
98
+ * @access private
99
+ */
100
+ var $key = "\0";
101
+
102
+ /**
103
+ * The Key Stream for encryption
104
+ *
105
+ * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
106
+ *
107
+ * @see Crypt_RC4::setKey()
108
+ * @var Array
109
+ * @access private
110
+ */
111
+ var $encryptStream = false;
112
+
113
+ /**
114
+ * The Key Stream for decryption
115
+ *
116
+ * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
117
+ *
118
+ * @see Crypt_RC4::setKey()
119
+ * @var Array
120
+ * @access private
121
+ */
122
+ var $decryptStream = false;
123
+
124
+ /**
125
+ * The $i and $j indexes for encryption
126
+ *
127
+ * @see Crypt_RC4::_crypt()
128
+ * @var Integer
129
+ * @access private
130
+ */
131
+ var $encryptIndex = 0;
132
+
133
+ /**
134
+ * The $i and $j indexes for decryption
135
+ *
136
+ * @see Crypt_RC4::_crypt()
137
+ * @var Integer
138
+ * @access private
139
+ */
140
+ var $decryptIndex = 0;
141
+
142
+ /**
143
+ * MCrypt parameters
144
+ *
145
+ * @see Crypt_RC4::setMCrypt()
146
+ * @var Array
147
+ * @access private
148
+ */
149
+ var $mcrypt = array('', '');
150
+
151
+ /**
152
+ * The Encryption Algorithm
153
+ *
154
+ * Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
155
+ *
156
+ * @see Crypt_RC4::Crypt_RC4()
157
+ * @var Integer
158
+ * @access private
159
+ */
160
+ var $mode;
161
+
162
+ /**
163
+ * Default Constructor.
164
+ *
165
+ * Determines whether or not the mcrypt extension should be used.
166
+ *
167
+ * @param optional Integer $mode
168
+ * @return Crypt_RC4
169
+ * @access public
170
+ */
171
+ function Crypt_RC4()
172
+ {
173
+ if ( !defined('CRYPT_RC4_MODE') ) {
174
+ switch (true) {
175
+ case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')):
176
+ // i'd check to see if rc4 was supported, by doing in_array('arcfour', mcrypt_list_algorithms('')),
177
+ // but since that can be changed after the object has been created, there doesn't seem to be
178
+ // a lot of point...
179
+ define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT);
180
+ break;
181
+ default:
182
+ define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL);
183
+ }
184
+ }
185
+
186
+ switch ( CRYPT_RC4_MODE ) {
187
+ case CRYPT_RC4_MODE_MCRYPT:
188
+ switch (true) {
189
+ case defined('MCRYPT_ARCFOUR'):
190
+ $this->mode = MCRYPT_ARCFOUR;
191
+ break;
192
+ case defined('MCRYPT_RC4');
193
+ $this->mode = MCRYPT_RC4;
194
+ }
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Sets the key.
200
+ *
201
+ * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
202
+ * be used. If no key is explicitly set, it'll be assumed to be a single null byte.
203
+ *
204
+ * @access public
205
+ * @param String $key
206
+ */
207
+ function setKey($key)
208
+ {
209
+ $this->key = $key;
210
+
211
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
212
+ return;
213
+ }
214
+
215
+ $keyLength = strlen($key);
216
+ $keyStream = array();
217
+ for ($i = 0; $i < 256; $i++) {
218
+ $keyStream[$i] = $i;
219
+ }
220
+ $j = 0;
221
+ for ($i = 0; $i < 256; $i++) {
222
+ $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
223
+ $temp = $keyStream[$i];
224
+ $keyStream[$i] = $keyStream[$j];
225
+ $keyStream[$j] = $temp;
226
+ }
227
+
228
+ $this->encryptIndex = $this->decryptIndex = array(0, 0);
229
+ $this->encryptStream = $this->decryptStream = $keyStream;
230
+ }
231
+
232
+ /**
233
+ * Dummy function.
234
+ *
235
+ * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
236
+ * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
237
+ * calling setKey().
238
+ *
239
+ * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
240
+ * the IV's are relatively easy to predict, an attack described by
241
+ * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
242
+ * can be used to quickly guess at the rest of the key. The following links elaborate:
243
+ *
244
+ * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
245
+ * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
246
+ *
247
+ * @param String $iv
248
+ * @see Crypt_RC4::setKey()
249
+ * @access public
250
+ */
251
+ function setIV($iv)
252
+ {
253
+ }
254
+
255
+ /**
256
+ * Sets MCrypt parameters. (optional)
257
+ *
258
+ * If MCrypt is being used, empty strings will be used, unless otherwise specified.
259
+ *
260
+ * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open
261
+ * @access public
262
+ * @param optional Integer $algorithm_directory
263
+ * @param optional Integer $mode_directory
264
+ */
265
+ function setMCrypt($algorithm_directory = '', $mode_directory = '')
266
+ {
267
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
268
+ $this->mcrypt = array($algorithm_directory, $mode_directory);
269
+ $this->_closeMCrypt();
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Encrypts a message.
275
+ *
276
+ * @see Crypt_RC4::_crypt()
277
+ * @access public
278
+ * @param String $plaintext
279
+ */
280
+ function encrypt($plaintext)
281
+ {
282
+ return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
283
+ }
284
+
285
+ /**
286
+ * Decrypts a message.
287
+ *
288
+ * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
289
+ * Atleast if the continuous buffer is disabled.
290
+ *
291
+ * @see Crypt_RC4::_crypt()
292
+ * @access public
293
+ * @param String $ciphertext
294
+ */
295
+ function decrypt($ciphertext)
296
+ {
297
+ return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
298
+ }
299
+
300
+ /**
301
+ * Encrypts or decrypts a message.
302
+ *
303
+ * @see Crypt_RC4::encrypt()
304
+ * @see Crypt_RC4::decrypt()
305
+ * @access private
306
+ * @param String $text
307
+ * @param Integer $mode
308
+ */
309
+ function _crypt($text, $mode)
310
+ {
311
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
312
+ $keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream';
313
+
314
+ if ($this->$keyStream === false) {
315
+ $this->$keyStream = mcrypt_module_open($this->mode, $this->mcrypt[0], MCRYPT_MODE_STREAM, $this->mcrypt[1]);
316
+ mcrypt_generic_init($this->$keyStream, $this->key, '');
317
+ } else if (!$this->continuousBuffer) {
318
+ mcrypt_generic_init($this->$keyStream, $this->key, '');
319
+ }
320
+ $newText = mcrypt_generic($this->$keyStream, $text);
321
+ if (!$this->continuousBuffer) {
322
+ mcrypt_generic_deinit($this->$keyStream);
323
+ }
324
+
325
+ return $newText;
326
+ }
327
+
328
+ if ($this->encryptStream === false) {
329
+ $this->setKey($this->key);
330
+ }
331
+
332
+ switch ($mode) {
333
+ case CRYPT_RC4_ENCRYPT:
334
+ $keyStream = $this->encryptStream;
335
+ list($i, $j) = $this->encryptIndex;
336
+ break;
337
+ case CRYPT_RC4_DECRYPT:
338
+ $keyStream = $this->decryptStream;
339
+ list($i, $j) = $this->decryptIndex;
340
+ }
341
+
342
+ $newText = '';
343
+ for ($k = 0; $k < strlen($text); $k++) {
344
+ $i = ($i + 1) & 255;
345
+ $j = ($j + $keyStream[$i]) & 255;
346
+ $temp = $keyStream[$i];
347
+ $keyStream[$i] = $keyStream[$j];
348
+ $keyStream[$j] = $temp;
349
+ $temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];
350
+ $newText.= chr(ord($text[$k]) ^ $temp);
351
+ }
352
+
353
+ if ($this->continuousBuffer) {
354
+ switch ($mode) {
355
+ case CRYPT_RC4_ENCRYPT:
356
+ $this->encryptStream = $keyStream;
357
+ $this->encryptIndex = array($i, $j);
358
+ break;
359
+ case CRYPT_RC4_DECRYPT:
360
+ $this->decryptStream = $keyStream;
361
+ $this->decryptIndex = array($i, $j);
362
+ }
363
+ }
364
+
365
+ return $newText;
366
+ }
367
+
368
+ /**
369
+ * Treat consecutive "packets" as if they are a continuous buffer.
370
+ *
371
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
372
+ * will yield different outputs:
373
+ *
374
+ * <code>
375
+ * echo $rc4->encrypt(substr($plaintext, 0, 8));
376
+ * echo $rc4->encrypt(substr($plaintext, 8, 8));
377
+ * </code>
378
+ * <code>
379
+ * echo $rc4->encrypt($plaintext);
380
+ * </code>
381
+ *
382
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
383
+ * another, as demonstrated with the following:
384
+ *
385
+ * <code>
386
+ * $rc4->encrypt(substr($plaintext, 0, 8));
387
+ * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
388
+ * </code>
389
+ * <code>
390
+ * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
391
+ * </code>
392
+ *
393
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
394
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
395
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
396
+ *
397
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
398
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
399
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
400
+ * however, they are also less intuitive and more likely to cause you problems.
401
+ *
402
+ * @see Crypt_RC4::disableContinuousBuffer()
403
+ * @access public
404
+ */
405
+ function enableContinuousBuffer()
406
+ {
407
+ $this->continuousBuffer = true;
408
+ }
409
+
410
+ /**
411
+ * Treat consecutive packets as if they are a discontinuous buffer.
412
+ *
413
+ * The default behavior.
414
+ *
415
+ * @see Crypt_RC4::enableContinuousBuffer()
416
+ * @access public
417
+ */
418
+ function disableContinuousBuffer()
419
+ {
420
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) {
421
+ $this->encryptIndex = $this->decryptIndex = array(0, 0);
422
+ $this->setKey($this->key);
423
+ }
424
+
425
+ $this->continuousBuffer = false;
426
+ }
427
+
428
+ /**
429
+ * Dummy function.
430
+ *
431
+ * Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
432
+ * included is so that you can switch between a block cipher and a stream cipher transparently.
433
+ *
434
+ * @see Crypt_RC4::disablePadding()
435
+ * @access public
436
+ */
437
+ function enablePadding()
438
+ {
439
+ }
440
+
441
+ /**
442
+ * Dummy function.
443
+ *
444
+ * @see Crypt_RC4::enablePadding()
445
+ * @access public
446
+ */
447
+ function disablePadding()
448
+ {
449
+ }
450
+
451
+ /**
452
+ * Class destructor.
453
+ *
454
+ * Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really
455
+ * needs to be called if mcrypt is being used.
456
+ *
457
+ * @access public
458
+ */
459
+ function __destruct()
460
+ {
461
+ if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
462
+ $this->_closeMCrypt();
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Properly close the MCrypt objects.
468
+ *
469
+ * @access prviate
470
+ */
471
+ function _closeMCrypt()
472
+ {
473
+ if ( $this->encryptStream !== false ) {
474
+ if ( $this->continuousBuffer ) {
475
+ mcrypt_generic_deinit($this->encryptStream);
476
+ }
477
+
478
+ mcrypt_module_close($this->encryptStream);
479
+
480
+ $this->encryptStream = false;
481
+ }
482
+
483
+ if ( $this->decryptStream !== false ) {
484
+ if ( $this->continuousBuffer ) {
485
+ mcrypt_generic_deinit($this->decryptStream);
486
+ }
487
+
488
+ mcrypt_module_close($this->decryptStream);
489
+
490
+ $this->decryptStream = false;
491
+ }
492
+ }
493
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/RSA.php ADDED
@@ -0,0 +1,2119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Here's an example of how to encrypt and decrypt text with this library:
10
+ * <code>
11
+ * <?php
12
+ * include('Crypt/RSA.php');
13
+ *
14
+ * $rsa = new Crypt_RSA();
15
+ * extract($rsa->createKey());
16
+ *
17
+ * $plaintext = 'terrafrost';
18
+ *
19
+ * $rsa->loadKey($privatekey);
20
+ * $ciphertext = $rsa->encrypt($plaintext);
21
+ *
22
+ * $rsa->loadKey($publickey);
23
+ * echo $rsa->decrypt($ciphertext);
24
+ * ?>
25
+ * </code>
26
+ *
27
+ * Here's an example of how to create signatures and verify signatures with this library:
28
+ * <code>
29
+ * <?php
30
+ * include('Crypt/RSA.php');
31
+ *
32
+ * $rsa = new Crypt_RSA();
33
+ * extract($rsa->createKey());
34
+ *
35
+ * $plaintext = 'terrafrost';
36
+ *
37
+ * $rsa->loadKey($privatekey);
38
+ * $signature = $rsa->sign($plaintext);
39
+ *
40
+ * $rsa->loadKey($publickey);
41
+ * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
42
+ * ?>
43
+ * </code>
44
+ *
45
+ * LICENSE: This library is free software; you can redistribute it and/or
46
+ * modify it under the terms of the GNU Lesser General Public
47
+ * License as published by the Free Software Foundation; either
48
+ * version 2.1 of the License, or (at your option) any later version.
49
+ *
50
+ * This library is distributed in the hope that it will be useful,
51
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
52
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
53
+ * Lesser General Public License for more details.
54
+ *
55
+ * You should have received a copy of the GNU Lesser General Public
56
+ * License along with this library; if not, write to the Free Software
57
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
58
+ * MA 02111-1307 USA
59
+ *
60
+ * @category Crypt
61
+ * @package Crypt_RSA
62
+ * @author Jim Wigginton <terrafrost@php.net>
63
+ * @copyright MMIX Jim Wigginton
64
+ * @license http://www.gnu.org/licenses/lgpl.txt
65
+ * @version $Id: RSA.php,v 1.15 2010/04/10 15:57:02 terrafrost Exp $
66
+ * @link http://phpseclib.sourceforge.net
67
+ */
68
+
69
+ /**
70
+ * Include Math_BigInteger
71
+ */
72
+ require_once('Math/BigInteger.php');
73
+
74
+ /**
75
+ * Include Crypt_Random
76
+ */
77
+ require_once('Crypt/Random.php');
78
+
79
+ /**
80
+ * Include Crypt_Hash
81
+ */
82
+ require_once('Crypt/Hash.php');
83
+
84
+ /**#@+
85
+ * @access public
86
+ * @see Crypt_RSA::encrypt()
87
+ * @see Crypt_RSA::decrypt()
88
+ */
89
+ /**
90
+ * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
91
+ * (OAEP) for encryption / decryption.
92
+ *
93
+ * Uses sha1 by default.
94
+ *
95
+ * @see Crypt_RSA::setHash()
96
+ * @see Crypt_RSA::setMGFHash()
97
+ */
98
+ define('CRYPT_RSA_ENCRYPTION_OAEP', 1);
99
+ /**
100
+ * Use PKCS#1 padding.
101
+ *
102
+ * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
103
+ * compatability with protocols (like SSH-1) written before OAEP's introduction.
104
+ */
105
+ define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
106
+ /**#@-*/
107
+
108
+ /**#@+
109
+ * @access public
110
+ * @see Crypt_RSA::sign()
111
+ * @see Crypt_RSA::verify()
112
+ * @see Crypt_RSA::setHash()
113
+ */
114
+ /**
115
+ * Use the Probabilistic Signature Scheme for signing
116
+ *
117
+ * Uses sha1 by default.
118
+ *
119
+ * @see Crypt_RSA::setSaltLength()
120
+ * @see Crypt_RSA::setMGFHash()
121
+ */
122
+ define('CRYPT_RSA_SIGNATURE_PSS', 1);
123
+ /**
124
+ * Use the PKCS#1 scheme by default.
125
+ *
126
+ * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
127
+ * compatability with protocols (like SSH-2) written before PSS's introduction.
128
+ */
129
+ define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
130
+ /**#@-*/
131
+
132
+ /**#@+
133
+ * @access private
134
+ * @see Crypt_RSA::createKey()
135
+ */
136
+ /**
137
+ * ASN1 Integer
138
+ */
139
+ define('CRYPT_RSA_ASN1_INTEGER', 2);
140
+ /**
141
+ * ASN1 Sequence (with the constucted bit set)
142
+ */
143
+ define('CRYPT_RSA_ASN1_SEQUENCE', 48);
144
+ /**#@-*/
145
+
146
+ /**#@+
147
+ * @access private
148
+ * @see Crypt_RSA::Crypt_RSA()
149
+ */
150
+ /**
151
+ * To use the pure-PHP implementation
152
+ */
153
+ define('CRYPT_RSA_MODE_INTERNAL', 1);
154
+ /**
155
+ * To use the OpenSSL library
156
+ *
157
+ * (if enabled; otherwise, the internal implementation will be used)
158
+ */
159
+ define('CRYPT_RSA_MODE_OPENSSL', 2);
160
+ /**#@-*/
161
+
162
+ /**#@+
163
+ * @access public
164
+ * @see Crypt_RSA::createKey()
165
+ * @see Crypt_RSA::setPrivateKeyFormat()
166
+ */
167
+ /**
168
+ * PKCS#1 formatted private key
169
+ *
170
+ * Used by OpenSSH
171
+ */
172
+ define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
173
+ /**#@-*/
174
+
175
+ /**#@+
176
+ * @access public
177
+ * @see Crypt_RSA::createKey()
178
+ * @see Crypt_RSA::setPublicKeyFormat()
179
+ */
180
+ /**
181
+ * Raw public key
182
+ *
183
+ * An array containing two Math_BigInteger objects.
184
+ *
185
+ * The exponent can be indexed with any of the following:
186
+ *
187
+ * 0, e, exponent, publicExponent
188
+ *
189
+ * The modulus can be indexed with any of the following:
190
+ *
191
+ * 1, n, modulo, modulus
192
+ */
193
+ define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 1);
194
+ /**
195
+ * PKCS#1 formatted public key
196
+ */
197
+ define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 2);
198
+ /**
199
+ * OpenSSH formatted public key
200
+ *
201
+ * Place in $HOME/.ssh/authorized_keys
202
+ */
203
+ define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 3);
204
+ /**#@-*/
205
+
206
+ /**
207
+ * Pure-PHP PKCS#1 compliant implementation of RSA.
208
+ *
209
+ * @author Jim Wigginton <terrafrost@php.net>
210
+ * @version 0.1.0
211
+ * @access public
212
+ * @package Crypt_RSA
213
+ */
214
+ class Crypt_RSA {
215
+ /**
216
+ * Precomputed Zero
217
+ *
218
+ * @var Array
219
+ * @access private
220
+ */
221
+ var $zero;
222
+
223
+ /**
224
+ * Precomputed One
225
+ *
226
+ * @var Array
227
+ * @access private
228
+ */
229
+ var $one;
230
+
231
+ /**
232
+ * Private Key Format
233
+ *
234
+ * @var Integer
235
+ * @access private
236
+ */
237
+ var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
238
+
239
+ /**
240
+ * Public Key Format
241
+ *
242
+ * @var Integer
243
+ * @access public
244
+ */
245
+ var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1;
246
+
247
+ /**
248
+ * Modulus (ie. n)
249
+ *
250
+ * @var Math_BigInteger
251
+ * @access private
252
+ */
253
+ var $modulus;
254
+
255
+ /**
256
+ * Modulus length
257
+ *
258
+ * @var Math_BigInteger
259
+ * @access private
260
+ */
261
+ var $k;
262
+
263
+ /**
264
+ * Exponent (ie. e or d)
265
+ *
266
+ * @var Math_BigInteger
267
+ * @access private
268
+ */
269
+ var $exponent;
270
+
271
+ /**
272
+ * Primes for Chinese Remainder Theorem (ie. p and q)
273
+ *
274
+ * @var Array
275
+ * @access private
276
+ */
277
+ var $primes;
278
+
279
+ /**
280
+ * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
281
+ *
282
+ * @var Array
283
+ * @access private
284
+ */
285
+ var $exponents;
286
+
287
+ /**
288
+ * Coefficients for Chinese Remainder Theorem (ie. qInv)
289
+ *
290
+ * @var Array
291
+ * @access private
292
+ */
293
+ var $coefficients;
294
+
295
+ /**
296
+ * Hash name
297
+ *
298
+ * @var String
299
+ * @access private
300
+ */
301
+ var $hashName;
302
+
303
+ /**
304
+ * Hash function
305
+ *
306
+ * @var Crypt_Hash
307
+ * @access private
308
+ */
309
+ var $hash;
310
+
311
+ /**
312
+ * Length of hash function output
313
+ *
314
+ * @var Integer
315
+ * @access private
316
+ */
317
+ var $hLen;
318
+
319
+ /**
320
+ * Length of salt
321
+ *
322
+ * @var Integer
323
+ * @access private
324
+ */
325
+ var $sLen;
326
+
327
+ /**
328
+ * Hash function for the Mask Generation Function
329
+ *
330
+ * @var Crypt_Hash
331
+ * @access private
332
+ */
333
+ var $mgfHash;
334
+
335
+ /**
336
+ * Length of MGF hash function output
337
+ *
338
+ * @var Integer
339
+ * @access private
340
+ */
341
+ var $mgfHLen;
342
+
343
+ /**
344
+ * Encryption mode
345
+ *
346
+ * @var Integer
347
+ * @access private
348
+ */
349
+ var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;
350
+
351
+ /**
352
+ * Signature mode
353
+ *
354
+ * @var Integer
355
+ * @access private
356
+ */
357
+ var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;
358
+
359
+ /**
360
+ * Public Exponent
361
+ *
362
+ * @var Mixed
363
+ * @access private
364
+ */
365
+ var $publicExponent = false;
366
+
367
+ /**
368
+ * Password
369
+ *
370
+ * @var String
371
+ * @access private
372
+ */
373
+ var $password = '';
374
+
375
+ /**
376
+ * The constructor
377
+ *
378
+ * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
379
+ * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
380
+ * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
381
+ *
382
+ * @return Crypt_RSA
383
+ * @access public
384
+ */
385
+ function Crypt_RSA()
386
+ {
387
+ if ( !defined('CRYPT_RSA_MODE') ) {
388
+ switch (true) {
389
+ //case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='):
390
+ // define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
391
+ // break;
392
+ default:
393
+ define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
394
+ }
395
+ }
396
+
397
+ $this->zero = new Math_BigInteger();
398
+ $this->one = new Math_BigInteger(1);
399
+
400
+ $this->hash = new Crypt_Hash('sha1');
401
+ $this->hLen = $this->hash->getLength();
402
+ $this->hashName = 'sha1';
403
+ $this->mgfHash = new Crypt_Hash('sha1');
404
+ $this->mgfHLen = $this->mgfHash->getLength();
405
+ }
406
+
407
+ /**
408
+ * Create public / private key pair
409
+ *
410
+ * Returns an array with the following three elements:
411
+ * - 'privatekey': The private key.
412
+ * - 'publickey': The public key.
413
+ * - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
414
+ * Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
415
+ *
416
+ * @access public
417
+ * @param optional Integer $bits
418
+ * @param optional Integer $timeout
419
+ * @param optional Math_BigInteger $p
420
+ */
421
+ function createKey($bits = 1024, $timeout = false, $partial = array())
422
+ {
423
+ if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL ) {
424
+ $rsa = openssl_pkey_new(array('private_key_bits' => $bits));
425
+ openssl_pkey_export($rsa, $privatekey);
426
+ $publickey = openssl_pkey_get_details($rsa);
427
+ $publickey = $publickey['key'];
428
+
429
+ if ($this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_PKCS1) {
430
+ $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
431
+ $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
432
+ }
433
+
434
+ return array(
435
+ 'privatekey' => $privatekey,
436
+ 'publickey' => $publickey,
437
+ 'partialkey' => false
438
+ );
439
+ }
440
+
441
+ static $e;
442
+ if (!isset($e)) {
443
+ if (!defined('CRYPT_RSA_EXPONENT')) {
444
+ // http://en.wikipedia.org/wiki/65537_%28number%29
445
+ define('CRYPT_RSA_EXPONENT', '65537');
446
+ }
447
+ if (!defined('CRYPT_RSA_COMMENT')) {
448
+ define('CRYPT_RSA_COMMENT', 'phpseclib-generated-key');
449
+ }
450
+ // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
451
+ // than 256 bits.
452
+ if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
453
+ define('CRYPT_RSA_SMALLEST_PRIME', 4096);
454
+ }
455
+
456
+ $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
457
+ }
458
+
459
+ extract($this->_generateMinMax($bits));
460
+ $absoluteMin = $min;
461
+ $temp = $bits >> 1;
462
+ if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
463
+ $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
464
+ $temp = CRYPT_RSA_SMALLEST_PRIME;
465
+ } else {
466
+ $num_primes = 2;
467
+ }
468
+ extract($this->_generateMinMax($temp + $bits % $temp));
469
+ $finalMax = $max;
470
+ extract($this->_generateMinMax($temp));
471
+
472
+ $generator = new Math_BigInteger();
473
+ $generator->setRandomGenerator('crypt_random');
474
+
475
+ $n = $this->one->copy();
476
+ if (!empty($partial)) {
477
+ extract(unserialize($partial));
478
+ } else {
479
+ $exponents = $coefficients = $primes = array();
480
+ $lcm = array(
481
+ 'top' => $this->one->copy(),
482
+ 'bottom' => false
483
+ );
484
+ }
485
+
486
+ $start = time();
487
+ $i0 = count($primes) + 1;
488
+
489
+ do {
490
+ for ($i = $i0; $i <= $num_primes; $i++) {
491
+ if ($timeout !== false) {
492
+ $timeout-= time() - $start;
493
+ $start = time();
494
+ if ($timeout <= 0) {
495
+ return serialize(array(
496
+ 'privatekey' => '',
497
+ 'publickey' => '',
498
+ 'partialkey' => array(
499
+ 'primes' => $primes,
500
+ 'coefficients' => $coefficients,
501
+ 'lcm' => $lcm,
502
+ 'exponents' => $exponents
503
+ )
504
+ ));
505
+ }
506
+ }
507
+
508
+ if ($i == $num_primes) {
509
+ list($min, $temp) = $absoluteMin->divide($n);
510
+ if (!$temp->equals($this->zero)) {
511
+ $min = $min->add($this->one); // ie. ceil()
512
+ }
513
+ $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
514
+ } else {
515
+ $primes[$i] = $generator->randomPrime($min, $max, $timeout);
516
+ }
517
+
518
+ if ($primes[$i] === false) { // if we've reached the timeout
519
+ return array(
520
+ 'privatekey' => '',
521
+ 'publickey' => '',
522
+ 'partialkey' => empty($primes) ? '' : serialize(array(
523
+ 'primes' => array_slice($primes, 0, $i - 1),
524
+ 'coefficients' => $coefficients,
525
+ 'lcm' => $lcm,
526
+ 'exponents' => $exponents
527
+ ))
528
+ );
529
+ }
530
+
531
+ // the first coefficient is calculated differently from the rest
532
+ // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
533
+ if ($i > 2) {
534
+ $coefficients[$i] = $n->modInverse($primes[$i]);
535
+ }
536
+
537
+ $n = $n->multiply($primes[$i]);
538
+
539
+ $temp = $primes[$i]->subtract($this->one);
540
+
541
+ // textbook RSA implementations use Euler's totient function instead of the least common multiple.
542
+ // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
543
+ $lcm['top'] = $lcm['top']->multiply($temp);
544
+ $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
545
+
546
+ $exponents[$i] = $e->modInverse($temp);
547
+ }
548
+
549
+ list($lcm) = $lcm['top']->divide($lcm['bottom']);
550
+ $gcd = $lcm->gcd($e);
551
+ $i0 = 1;
552
+ } while (!$gcd->equals($this->one));
553
+
554
+ $d = $e->modInverse($lcm);
555
+
556
+ $coefficients[2] = $primes[2]->modInverse($primes[1]);
557
+
558
+ // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
559
+ // RSAPrivateKey ::= SEQUENCE {
560
+ // version Version,
561
+ // modulus INTEGER, -- n
562
+ // publicExponent INTEGER, -- e
563
+ // privateExponent INTEGER, -- d
564
+ // prime1 INTEGER, -- p
565
+ // prime2 INTEGER, -- q
566
+ // exponent1 INTEGER, -- d mod (p-1)
567
+ // exponent2 INTEGER, -- d mod (q-1)
568
+ // coefficient INTEGER, -- (inverse of q) mod p
569
+ // otherPrimeInfos OtherPrimeInfos OPTIONAL
570
+ // }
571
+
572
+ return array(
573
+ 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
574
+ 'publickey' => $this->_convertPublicKey($n, $e),
575
+ 'partialkey' => false
576
+ );
577
+ }
578
+
579
+ /**
580
+ * Convert a private key to the appropriate format.
581
+ *
582
+ * @access private
583
+ * @see setPrivateKeyFormat()
584
+ * @param String $RSAPrivateKey
585
+ * @return String
586
+ */
587
+ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
588
+ {
589
+ $num_primes = count($primes);
590
+ $raw = array(
591
+ 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
592
+ 'modulus' => $n->toBytes(true),
593
+ 'publicExponent' => $e->toBytes(true),
594
+ 'privateExponent' => $d->toBytes(true),
595
+ 'prime1' => $primes[1]->toBytes(true),
596
+ 'prime2' => $primes[2]->toBytes(true),
597
+ 'exponent1' => $exponents[1]->toBytes(true),
598
+ 'exponent2' => $exponents[2]->toBytes(true),
599
+ 'coefficient' => $coefficients[2]->toBytes(true)
600
+ );
601
+
602
+ // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
603
+ // call _convertPublicKey() instead.
604
+ switch ($this->privateKeyFormat) {
605
+ default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
606
+ $components = array();
607
+ foreach ($raw as $name => $value) {
608
+ $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
609
+ }
610
+
611
+ $RSAPrivateKey = implode('', $components);
612
+
613
+ if ($num_primes > 2) {
614
+ $OtherPrimeInfos = '';
615
+ for ($i = 3; $i <= $num_primes; $i++) {
616
+ // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
617
+ //
618
+ // OtherPrimeInfo ::= SEQUENCE {
619
+ // prime INTEGER, -- ri
620
+ // exponent INTEGER, -- di
621
+ // coefficient INTEGER -- ti
622
+ // }
623
+ $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
624
+ $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
625
+ $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
626
+ $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
627
+ }
628
+ $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
629
+ }
630
+
631
+ $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
632
+
633
+ if (!empty($this->password)) {
634
+ $iv = $this->_random(8);
635
+ $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
636
+ $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
637
+ if (!class_exists('Crypt_TripleDES')) {
638
+ require_once('Crypt/TripleDES.php');
639
+ }
640
+ $des = new Crypt_TripleDES();
641
+ $des->setKey($symkey);
642
+ $des->setIV($iv);
643
+ $iv = strtoupper(bin2hex($iv));
644
+ $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
645
+ "Proc-Type: 4,ENCRYPTED\r\n" .
646
+ "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
647
+ "\r\n" .
648
+ chunk_split(base64_encode($des->encrypt($RSAPrivateKey))) .
649
+ '-----END RSA PRIVATE KEY-----';
650
+ } else {
651
+ $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
652
+ chunk_split(base64_encode($RSAPrivateKey)) .
653
+ '-----END RSA PRIVATE KEY-----';
654
+ }
655
+
656
+ return $RSAPrivateKey;
657
+ }
658
+ }
659
+
660
+ /**
661
+ * Convert a public key to the appropriate format
662
+ *
663
+ * @access private
664
+ * @see setPublicKeyFormat()
665
+ * @param String $RSAPrivateKey
666
+ * @return String
667
+ */
668
+ function _convertPublicKey($n, $e)
669
+ {
670
+ $modulus = $n->toBytes(true);
671
+ $publicExponent = $e->toBytes(true);
672
+
673
+ switch ($this->publicKeyFormat) {
674
+ case CRYPT_RSA_PUBLIC_FORMAT_RAW:
675
+ return array('e' => $e->copy(), 'n' => $n->copy());
676
+ case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
677
+ // from <http://tools.ietf.org/html/rfc4253#page-15>:
678
+ // string "ssh-rsa"
679
+ // mpint e
680
+ // mpint n
681
+ $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
682
+ $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . CRYPT_RSA_COMMENT;
683
+
684
+ return $RSAPublicKey;
685
+ default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1
686
+ // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
687
+ // RSAPublicKey ::= SEQUENCE {
688
+ // modulus INTEGER, -- n
689
+ // publicExponent INTEGER -- e
690
+ // }
691
+ $components = array(
692
+ 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
693
+ 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
694
+ );
695
+
696
+ $RSAPublicKey = pack('Ca*a*a*',
697
+ CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
698
+ $components['modulus'], $components['publicExponent']
699
+ );
700
+
701
+ $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
702
+ chunk_split(base64_encode($RSAPublicKey)) .
703
+ '-----END PUBLIC KEY-----';
704
+
705
+ return $RSAPublicKey;
706
+ }
707
+ }
708
+
709
+ /**
710
+ * Break a public or private key down into its constituant components
711
+ *
712
+ * @access private
713
+ * @see _convertPublicKey()
714
+ * @see _convertPrivateKey()
715
+ * @param String $key
716
+ * @param Integer $type
717
+ * @return Array
718
+ */
719
+ function _parseKey($key, $type)
720
+ {
721
+ switch ($type) {
722
+ case CRYPT_RSA_PUBLIC_FORMAT_RAW:
723
+ if (!is_array($key)) {
724
+ return false;
725
+ }
726
+ $components = array();
727
+ switch (true) {
728
+ case isset($key['e']):
729
+ $components['publicExponent'] = $key['e']->copy();
730
+ break;
731
+ case isset($key['exponent']):
732
+ $components['publicExponent'] = $key['exponent']->copy();
733
+ break;
734
+ case isset($key['publicExponent']):
735
+ $components['publicExponent'] = $key['publicExponent']->copy();
736
+ break;
737
+ case isset($key[0]):
738
+ $components['publicExponent'] = $key[0]->copy();
739
+ }
740
+ switch (true) {
741
+ case isset($key['n']):
742
+ $components['modulus'] = $key['n']->copy();
743
+ break;
744
+ case isset($key['modulo']):
745
+ $components['modulus'] = $key['modulo']->copy();
746
+ break;
747
+ case isset($key['modulus']):
748
+ $components['modulus'] = $key['modulus']->copy();
749
+ break;
750
+ case isset($key[1]):
751
+ $components['modulus'] = $key[1]->copy();
752
+ }
753
+ return $components;
754
+ case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
755
+ case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
756
+ /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
757
+ "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
758
+ protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
759
+ two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
760
+
761
+ http://tools.ietf.org/html/rfc1421#section-4.6.1.1
762
+ http://tools.ietf.org/html/rfc1421#section-4.6.1.3
763
+
764
+ DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
765
+ DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
766
+ function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
767
+ own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
768
+ implementation are part of the standard, as well.
769
+
770
+ * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
771
+ if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
772
+ $iv = pack('H*', trim($matches[2]));
773
+ $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
774
+ $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
775
+ $ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-#s', '', $key);
776
+ $ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false;
777
+ if ($ciphertext === false) {
778
+ $ciphertext = $key;
779
+ }
780
+ switch ($matches[1]) {
781
+ case 'DES-EDE3-CBC':
782
+ if (!class_exists('Crypt_TripleDES')) {
783
+ require_once('Crypt/TripleDES.php');
784
+ }
785
+ $crypto = new Crypt_TripleDES();
786
+ break;
787
+ case 'DES-CBC':
788
+ if (!class_exists('Crypt_DES')) {
789
+ require_once('Crypt/DES.php');
790
+ }
791
+ $crypto = new Crypt_DES();
792
+ break;
793
+ default:
794
+ return false;
795
+ }
796
+ $crypto->setKey($symkey);
797
+ $crypto->setIV($iv);
798
+ $decoded = $crypto->decrypt($ciphertext);
799
+ } else {
800
+ $decoded = preg_replace('#-.+-|[\r\n]#', '', $key);
801
+ $decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
802
+ }
803
+
804
+ if ($decoded !== false) {
805
+ $key = $decoded;
806
+ }
807
+
808
+ $components = array();
809
+
810
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
811
+ return false;
812
+ }
813
+ if ($this->_decodeLength($key) != strlen($key)) {
814
+ return false;
815
+ }
816
+
817
+ $tag = ord($this->_string_shift($key));
818
+ if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
819
+ /* intended for keys for which OpenSSL's asn1parse returns the following:
820
+
821
+ 0:d=0 hl=4 l= 290 cons: SEQUENCE
822
+ 4:d=1 hl=2 l= 13 cons: SEQUENCE
823
+ 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
824
+ 17:d=2 hl=2 l= 0 prim: NULL
825
+ 19:d=1 hl=4 l= 271 prim: BIT STRING */
826
+ $this->_string_shift($key, $this->_decodeLength($key));
827
+ $this->_string_shift($key); // skip over the BIT STRING tag
828
+ $this->_decodeLength($key); // skip over the BIT STRING length
829
+ // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
830
+ // unused bits in teh final subsequent octet. The number shall be in the range zero to seven."
831
+ // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
832
+ $this->_string_shift($key);
833
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
834
+ return false;
835
+ }
836
+ if ($this->_decodeLength($key) != strlen($key)) {
837
+ return false;
838
+ }
839
+ $tag = ord($this->_string_shift($key));
840
+ }
841
+ if ($tag != CRYPT_RSA_ASN1_INTEGER) {
842
+ return false;
843
+ }
844
+
845
+ $length = $this->_decodeLength($key);
846
+ $temp = $this->_string_shift($key, $length);
847
+ if (strlen($temp) != 1 || ord($temp) > 2) {
848
+ $components['modulus'] = new Math_BigInteger($temp, -256);
849
+ $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
850
+ $length = $this->_decodeLength($key);
851
+ $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
852
+
853
+ return $components;
854
+ }
855
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
856
+ return false;
857
+ }
858
+ $length = $this->_decodeLength($key);
859
+ $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
860
+ $this->_string_shift($key);
861
+ $length = $this->_decodeLength($key);
862
+ $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
863
+ $this->_string_shift($key);
864
+ $length = $this->_decodeLength($key);
865
+ $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
866
+ $this->_string_shift($key);
867
+ $length = $this->_decodeLength($key);
868
+ $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
869
+ $this->_string_shift($key);
870
+ $length = $this->_decodeLength($key);
871
+ $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
872
+ $this->_string_shift($key);
873
+ $length = $this->_decodeLength($key);
874
+ $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
875
+ $this->_string_shift($key);
876
+ $length = $this->_decodeLength($key);
877
+ $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
878
+ $this->_string_shift($key);
879
+ $length = $this->_decodeLength($key);
880
+ $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), -256));
881
+
882
+ if (!empty($key)) {
883
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
884
+ return false;
885
+ }
886
+ $this->_decodeLength($key);
887
+ while (!empty($key)) {
888
+ if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
889
+ return false;
890
+ }
891
+ $this->_decodeLength($key);
892
+ $key = substr($key, 1);
893
+ $length = $this->_decodeLength($key);
894
+ $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
895
+ $this->_string_shift($key);
896
+ $length = $this->_decodeLength($key);
897
+ $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
898
+ $this->_string_shift($key);
899
+ $length = $this->_decodeLength($key);
900
+ $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
901
+ }
902
+ }
903
+
904
+ return $components;
905
+ case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
906
+ $key = base64_decode(preg_replace('#^ssh-rsa | .+$#', '', $key));
907
+ if ($key === false) {
908
+ return false;
909
+ }
910
+
911
+ $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
912
+
913
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
914
+ $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
915
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
916
+ $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
917
+
918
+ if ($cleanup && strlen($key)) {
919
+ extract(unpack('Nlength', $this->_string_shift($key, 4)));
920
+ return array(
921
+ 'modulus' => new Math_BigInteger($this->_string_shift($key, $length), -256),
922
+ 'publicExponent' => $modulus
923
+ );
924
+ } else {
925
+ return array(
926
+ 'modulus' => $modulus,
927
+ 'publicExponent' => $publicExponent
928
+ );
929
+ }
930
+ }
931
+ }
932
+
933
+ /**
934
+ * Loads a public or private key
935
+ *
936
+ * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
937
+ *
938
+ * @access public
939
+ * @param String $key
940
+ * @param Integer $type optional
941
+ */
942
+ function loadKey($key, $type = CRYPT_RSA_PRIVATE_FORMAT_PKCS1)
943
+ {
944
+ $components = $this->_parseKey($key, $type);
945
+ if ($components === false) {
946
+ return false;
947
+ }
948
+
949
+ $this->modulus = $components['modulus'];
950
+ $this->k = strlen($this->modulus->toBytes());
951
+ $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
952
+ if (isset($components['primes'])) {
953
+ $this->primes = $components['primes'];
954
+ $this->exponents = $components['exponents'];
955
+ $this->coefficients = $components['coefficients'];
956
+ $this->publicExponent = $components['publicExponent'];
957
+ } else {
958
+ $this->primes = array();
959
+ $this->exponents = array();
960
+ $this->coefficients = array();
961
+ $this->publicExponent = false;
962
+ }
963
+
964
+ return true;
965
+ }
966
+
967
+ /**
968
+ * Sets the password
969
+ *
970
+ * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
971
+ * Or rather, pass in $password such that empty($password) is true.
972
+ *
973
+ * @see createKey()
974
+ * @see loadKey()
975
+ * @access public
976
+ * @param String $password
977
+ */
978
+ function setPassword($password)
979
+ {
980
+ $this->password = $password;
981
+ }
982
+
983
+ /**
984
+ * Defines the public key
985
+ *
986
+ * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
987
+ * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
988
+ * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
989
+ * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
990
+ * exponent this won't work unless you manually add the public exponent.
991
+ *
992
+ * Do note that when a new key is loaded the index will be cleared.
993
+ *
994
+ * Returns true on success, false on failure
995
+ *
996
+ * @see getPublicKey()
997
+ * @access public
998
+ * @param String $key
999
+ * @param Integer $type optional
1000
+ * @return Boolean
1001
+ */
1002
+ function setPublicKey($key, $type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
1003
+ {
1004
+ $components = $this->_parseKey($key, $type);
1005
+ if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
1006
+ return false;
1007
+ }
1008
+ $this->publicExponent = $components['publicExponent'];
1009
+ }
1010
+
1011
+ /**
1012
+ * Returns the public key
1013
+ *
1014
+ * The public key is only returned under two circumstances - if the private key had the public key embedded within it
1015
+ * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
1016
+ * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
1017
+ *
1018
+ * @see getPublicKey()
1019
+ * @access public
1020
+ * @param String $key
1021
+ * @param Integer $type optional
1022
+ */
1023
+ function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
1024
+ {
1025
+ if (empty($this->modulus) || empty($this->publicExponent)) {
1026
+ return false;
1027
+ }
1028
+
1029
+ $oldFormat = $this->publicKeyFormat;
1030
+ $this->publicKeyFormat = $type;
1031
+ $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
1032
+ $this->publicKeyFormat = $oldFormat;
1033
+ return $temp;
1034
+ }
1035
+
1036
+ /**
1037
+ * Generates the smallest and largest numbers requiring $bits bits
1038
+ *
1039
+ * @access private
1040
+ * @param Integer $bits
1041
+ * @return Array
1042
+ */
1043
+ function _generateMinMax($bits)
1044
+ {
1045
+ $bytes = $bits >> 3;
1046
+ $min = str_repeat(chr(0), $bytes);
1047
+ $max = str_repeat(chr(0xFF), $bytes);
1048
+ $msb = $bits & 7;
1049
+ if ($msb) {
1050
+ $min = chr(1 << ($msb - 1)) . $min;
1051
+ $max = chr((1 << $msb) - 1) . $max;
1052
+ } else {
1053
+ $min[0] = chr(0x80);
1054
+ }
1055
+
1056
+ return array(
1057
+ 'min' => new Math_BigInteger($min, 256),
1058
+ 'max' => new Math_BigInteger($max, 256)
1059
+ );
1060
+ }
1061
+
1062
+ /**
1063
+ * DER-decode the length
1064
+ *
1065
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
1066
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 � 8.1.3} for more information.
1067
+ *
1068
+ * @access private
1069
+ * @param String $string
1070
+ * @return Integer
1071
+ */
1072
+ function _decodeLength(&$string)
1073
+ {
1074
+ $length = ord($this->_string_shift($string));
1075
+ if ( $length & 0x80 ) { // definite length, long form
1076
+ $length&= 0x7F;
1077
+ $temp = $this->_string_shift($string, $length);
1078
+ list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
1079
+ }
1080
+ return $length;
1081
+ }
1082
+
1083
+ /**
1084
+ * DER-encode the length
1085
+ *
1086
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
1087
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 � 8.1.3} for more information.
1088
+ *
1089
+ * @access private
1090
+ * @param Integer $length
1091
+ * @return String
1092
+ */
1093
+ function _encodeLength($length)
1094
+ {
1095
+ if ($length <= 0x7F) {
1096
+ return chr($length);
1097
+ }
1098
+
1099
+ $temp = ltrim(pack('N', $length), chr(0));
1100
+ return pack('Ca*', 0x80 | strlen($temp), $temp);
1101
+ }
1102
+
1103
+ /**
1104
+ * String Shift
1105
+ *
1106
+ * Inspired by array_shift
1107
+ *
1108
+ * @param String $string
1109
+ * @param optional Integer $index
1110
+ * @return String
1111
+ * @access private
1112
+ */
1113
+ function _string_shift(&$string, $index = 1)
1114
+ {
1115
+ $substr = substr($string, 0, $index);
1116
+ $string = substr($string, $index);
1117
+ return $substr;
1118
+ }
1119
+
1120
+ /**
1121
+ * Determines the private key format
1122
+ *
1123
+ * @see createKey()
1124
+ * @access public
1125
+ * @param Integer $format
1126
+ */
1127
+ function setPrivateKeyFormat($format)
1128
+ {
1129
+ $this->privateKeyFormat = $format;
1130
+ }
1131
+
1132
+ /**
1133
+ * Determines the public key format
1134
+ *
1135
+ * @see createKey()
1136
+ * @access public
1137
+ * @param Integer $format
1138
+ */
1139
+ function setPublicKeyFormat($format)
1140
+ {
1141
+ $this->publicKeyFormat = $format;
1142
+ }
1143
+
1144
+ /**
1145
+ * Determines which hashing function should be used
1146
+ *
1147
+ * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
1148
+ * decryption. If $hash isn't supported, sha1 is used.
1149
+ *
1150
+ * @access public
1151
+ * @param String $hash
1152
+ */
1153
+ function setHash($hash)
1154
+ {
1155
+ // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
1156
+ switch ($hash) {
1157
+ case 'md2':
1158
+ case 'md5':
1159
+ case 'sha1':
1160
+ case 'sha256':
1161
+ case 'sha384':
1162
+ case 'sha512':
1163
+ $this->hash = new Crypt_Hash($hash);
1164
+ $this->hashName = $hash;
1165
+ break;
1166
+ default:
1167
+ $this->hash = new Crypt_Hash('sha1');
1168
+ $this->hashName = 'sha1';
1169
+ }
1170
+ $this->hLen = $this->hash->getLength();
1171
+ }
1172
+
1173
+ /**
1174
+ * Determines which hashing function should be used for the mask generation function
1175
+ *
1176
+ * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
1177
+ * best if Hash and MGFHash are set to the same thing this is not a requirement.
1178
+ *
1179
+ * @access public
1180
+ * @param String $hash
1181
+ */
1182
+ function setMGFHash($hash)
1183
+ {
1184
+ // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
1185
+ switch ($hash) {
1186
+ case 'md2':
1187
+ case 'md5':
1188
+ case 'sha1':
1189
+ case 'sha256':
1190
+ case 'sha384':
1191
+ case 'sha512':
1192
+ $this->mgfHash = new Crypt_Hash($hash);
1193
+ break;
1194
+ default:
1195
+ $this->mgfHash = new Crypt_Hash('sha1');
1196
+ }
1197
+ $this->mgfHLen = $this->mgfHash->getLength();
1198
+ }
1199
+
1200
+ /**
1201
+ * Determines the salt length
1202
+ *
1203
+ * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
1204
+ *
1205
+ * Typical salt lengths in octets are hLen (the length of the output
1206
+ * of the hash function Hash) and 0.
1207
+ *
1208
+ * @access public
1209
+ * @param Integer $format
1210
+ */
1211
+ function setSaltLength($sLen)
1212
+ {
1213
+ $this->sLen = $sLen;
1214
+ }
1215
+
1216
+ /**
1217
+ * Generates a random string x bytes long
1218
+ *
1219
+ * @access public
1220
+ * @param Integer $bytes
1221
+ * @param optional Integer $nonzero
1222
+ * @return String
1223
+ */
1224
+ function _random($bytes, $nonzero = false)
1225
+ {
1226
+ $temp = '';
1227
+ if ($nonzero) {
1228
+ for ($i = 0; $i < $bytes; $i++) {
1229
+ $temp.= chr(crypt_random(1, 255));
1230
+ }
1231
+ } else {
1232
+ $ints = ($bytes + 1) >> 2;
1233
+ for ($i = 0; $i < $ints; $i++) {
1234
+ $temp.= pack('N', crypt_random());
1235
+ }
1236
+ $temp = substr($temp, 0, $bytes);
1237
+ }
1238
+ return $temp;
1239
+ }
1240
+
1241
+ /**
1242
+ * Integer-to-Octet-String primitive
1243
+ *
1244
+ * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
1245
+ *
1246
+ * @access private
1247
+ * @param Math_BigInteger $x
1248
+ * @param Integer $xLen
1249
+ * @return String
1250
+ */
1251
+ function _i2osp($x, $xLen)
1252
+ {
1253
+ $x = $x->toBytes();
1254
+ if (strlen($x) > $xLen) {
1255
+ user_error('Integer too large', E_USER_NOTICE);
1256
+ return false;
1257
+ }
1258
+ return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
1259
+ }
1260
+
1261
+ /**
1262
+ * Octet-String-to-Integer primitive
1263
+ *
1264
+ * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
1265
+ *
1266
+ * @access private
1267
+ * @param String $x
1268
+ * @return Math_BigInteger
1269
+ */
1270
+ function _os2ip($x)
1271
+ {
1272
+ return new Math_BigInteger($x, 256);
1273
+ }
1274
+
1275
+ /**
1276
+ * Exponentiate with or without Chinese Remainder Theorem
1277
+ *
1278
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
1279
+ *
1280
+ * @access private
1281
+ * @param Math_BigInteger $x
1282
+ * @return Math_BigInteger
1283
+ */
1284
+ function _exponentiate($x)
1285
+ {
1286
+ if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
1287
+ return $x->modPow($this->exponent, $this->modulus);
1288
+ }
1289
+
1290
+ $num_primes = count($this->primes);
1291
+
1292
+ if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
1293
+ $m_i = array(
1294
+ 1 => $x->modPow($this->exponents[1], $this->primes[1]),
1295
+ 2 => $x->modPow($this->exponents[2], $this->primes[2])
1296
+ );
1297
+ $h = $m_i[1]->subtract($m_i[2]);
1298
+ $h = $h->multiply($this->coefficients[2]);
1299
+ list(, $h) = $h->divide($this->primes[1]);
1300
+ $m = $m_i[2]->add($h->multiply($this->primes[2]));
1301
+
1302
+ $r = $this->primes[1];
1303
+ for ($i = 3; $i <= $num_primes; $i++) {
1304
+ $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
1305
+
1306
+ $r = $r->multiply($this->primes[$i - 1]);
1307
+
1308
+ $h = $m_i->subtract($m);
1309
+ $h = $h->multiply($this->coefficients[$i]);
1310
+ list(, $h) = $h->divide($this->primes[$i]);
1311
+
1312
+ $m = $m->add($r->multiply($h));
1313
+ }
1314
+ } else {
1315
+ $smallest = $this->primes[1];
1316
+ for ($i = 2; $i <= $num_primes; $i++) {
1317
+ if ($smallest->compare($this->primes[$i]) > 0) {
1318
+ $smallest = $this->primes[$i];
1319
+ }
1320
+ }
1321
+
1322
+ $one = new Math_BigInteger(1);
1323
+ $one->setRandomGenerator('crypt_random');
1324
+
1325
+ $r = $one->random($one, $smallest->subtract($one));
1326
+
1327
+ $m_i = array(
1328
+ 1 => $this->_blind($x, $r, 1),
1329
+ 2 => $this->_blind($x, $r, 2)
1330
+ );
1331
+ $h = $m_i[1]->subtract($m_i[2]);
1332
+ $h = $h->multiply($this->coefficients[2]);
1333
+ list(, $h) = $h->divide($this->primes[1]);
1334
+ $m = $m_i[2]->add($h->multiply($this->primes[2]));
1335
+
1336
+ $r = $this->primes[1];
1337
+ for ($i = 3; $i <= $num_primes; $i++) {
1338
+ $m_i = $this->_blind($x, $r, $i);
1339
+
1340
+ $r = $r->multiply($this->primes[$i - 1]);
1341
+
1342
+ $h = $m_i->subtract($m);
1343
+ $h = $h->multiply($this->coefficients[$i]);
1344
+ list(, $h) = $h->divide($this->primes[$i]);
1345
+
1346
+ $m = $m->add($r->multiply($h));
1347
+ }
1348
+ }
1349
+
1350
+ return $m;
1351
+ }
1352
+
1353
+ /**
1354
+ * Performs RSA Blinding
1355
+ *
1356
+ * Protects against timing attacks by employing RSA Blinding.
1357
+ * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
1358
+ *
1359
+ * @access private
1360
+ * @param Math_BigInteger $x
1361
+ * @param Math_BigInteger $r
1362
+ * @param Integer $i
1363
+ * @return Math_BigInteger
1364
+ */
1365
+ function _blind($x, $r, $i)
1366
+ {
1367
+ $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
1368
+
1369
+ $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
1370
+
1371
+ $r = $r->modInverse($this->primes[$i]);
1372
+ $x = $x->multiply($r);
1373
+ list(, $x) = $x->divide($this->primes[$i]);
1374
+
1375
+ return $x;
1376
+ }
1377
+
1378
+ /**
1379
+ * RSAEP
1380
+ *
1381
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
1382
+ *
1383
+ * @access private
1384
+ * @param Math_BigInteger $m
1385
+ * @return Math_BigInteger
1386
+ */
1387
+ function _rsaep($m)
1388
+ {
1389
+ if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
1390
+ user_error('Message representative out of range', E_USER_NOTICE);
1391
+ return false;
1392
+ }
1393
+ return $this->_exponentiate($m);
1394
+ }
1395
+
1396
+ /**
1397
+ * RSADP
1398
+ *
1399
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
1400
+ *
1401
+ * @access private
1402
+ * @param Math_BigInteger $c
1403
+ * @return Math_BigInteger
1404
+ */
1405
+ function _rsadp($c)
1406
+ {
1407
+ if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
1408
+ user_error('Ciphertext representative out of range', E_USER_NOTICE);
1409
+ return false;
1410
+ }
1411
+ return $this->_exponentiate($c);
1412
+ }
1413
+
1414
+ /**
1415
+ * RSASP1
1416
+ *
1417
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
1418
+ *
1419
+ * @access private
1420
+ * @param Math_BigInteger $m
1421
+ * @return Math_BigInteger
1422
+ */
1423
+ function _rsasp1($m)
1424
+ {
1425
+ if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
1426
+ user_error('Message representative out of range', E_USER_NOTICE);
1427
+ return false;
1428
+ }
1429
+ return $this->_exponentiate($m);
1430
+ }
1431
+
1432
+ /**
1433
+ * RSAVP1
1434
+ *
1435
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
1436
+ *
1437
+ * @access private
1438
+ * @param Math_BigInteger $s
1439
+ * @return Math_BigInteger
1440
+ */
1441
+ function _rsavp1($s)
1442
+ {
1443
+ if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
1444
+ user_error('Signature representative out of range', E_USER_NOTICE);
1445
+ return false;
1446
+ }
1447
+ return $this->_exponentiate($s);
1448
+ }
1449
+
1450
+ /**
1451
+ * MGF1
1452
+ *
1453
+ * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
1454
+ *
1455
+ * @access private
1456
+ * @param String $mgfSeed
1457
+ * @param Integer $mgfLen
1458
+ * @return String
1459
+ */
1460
+ function _mgf1($mgfSeed, $maskLen)
1461
+ {
1462
+ // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
1463
+
1464
+ $t = '';
1465
+ $count = ceil($maskLen / $this->mgfHLen);
1466
+ for ($i = 0; $i < $count; $i++) {
1467
+ $c = pack('N', $i);
1468
+ $t.= $this->mgfHash->hash($mgfSeed . $c);
1469
+ }
1470
+
1471
+ return substr($t, 0, $maskLen);
1472
+ }
1473
+
1474
+ /**
1475
+ * RSAES-OAEP-ENCRYPT
1476
+ *
1477
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
1478
+ * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
1479
+ *
1480
+ * @access private
1481
+ * @param String $m
1482
+ * @param String $l
1483
+ * @return String
1484
+ */
1485
+ function _rsaes_oaep_encrypt($m, $l = '')
1486
+ {
1487
+ $mLen = strlen($m);
1488
+
1489
+ // Length checking
1490
+
1491
+ // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1492
+ // be output.
1493
+
1494
+ if ($mLen > $this->k - 2 * $this->hLen - 2) {
1495
+ user_error('Message too long', E_USER_NOTICE);
1496
+ return false;
1497
+ }
1498
+
1499
+ // EME-OAEP encoding
1500
+
1501
+ $lHash = $this->hash->hash($l);
1502
+ $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
1503
+ $db = $lHash . $ps . chr(1) . $m;
1504
+ $seed = $this->_random($this->hLen);
1505
+ $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1506
+ $maskedDB = $db ^ $dbMask;
1507
+ $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1508
+ $maskedSeed = $seed ^ $seedMask;
1509
+ $em = chr(0) . $maskedSeed . $maskedDB;
1510
+
1511
+ // RSA encryption
1512
+
1513
+ $m = $this->_os2ip($em);
1514
+ $c = $this->_rsaep($m);
1515
+ $c = $this->_i2osp($c, $this->k);
1516
+
1517
+ // Output the ciphertext C
1518
+
1519
+ return $c;
1520
+ }
1521
+
1522
+ /**
1523
+ * RSAES-OAEP-DECRYPT
1524
+ *
1525
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
1526
+ * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
1527
+ *
1528
+ * Note. Care must be taken to ensure that an opponent cannot
1529
+ * distinguish the different error conditions in Step 3.g, whether by
1530
+ * error message or timing, or, more generally, learn partial
1531
+ * information about the encoded message EM. Otherwise an opponent may
1532
+ * be able to obtain useful information about the decryption of the
1533
+ * ciphertext C, leading to a chosen-ciphertext attack such as the one
1534
+ * observed by Manger [36].
1535
+ *
1536
+ * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
1537
+ *
1538
+ * Both the encryption and the decryption operations of RSAES-OAEP take
1539
+ * the value of a label L as input. In this version of PKCS #1, L is
1540
+ * the empty string; other uses of the label are outside the scope of
1541
+ * this document.
1542
+ *
1543
+ * @access private
1544
+ * @param String $c
1545
+ * @param String $l
1546
+ * @return String
1547
+ */
1548
+ function _rsaes_oaep_decrypt($c, $l = '')
1549
+ {
1550
+ // Length checking
1551
+
1552
+ // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1553
+ // be output.
1554
+
1555
+ if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
1556
+ user_error('Decryption error', E_USER_NOTICE);
1557
+ return false;
1558
+ }
1559
+
1560
+ // RSA decryption
1561
+
1562
+ $c = $this->_os2ip($c);
1563
+ $m = $this->_rsadp($c);
1564
+ if ($m === false) {
1565
+ user_error('Decryption error', E_USER_NOTICE);
1566
+ return false;
1567
+ }
1568
+ $em = $this->_i2osp($m, $this->k);
1569
+
1570
+ // EME-OAEP decoding
1571
+
1572
+ $lHash = $this->hash->hash($l);
1573
+ $y = ord($em[0]);
1574
+ $maskedSeed = substr($em, 1, $this->hLen);
1575
+ $maskedDB = substr($em, $this->hLen + 1);
1576
+ $seedMask = $this->_mgf1($maskedDB, $this->hLen);
1577
+ $seed = $maskedSeed ^ $seedMask;
1578
+ $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
1579
+ $db = $maskedDB ^ $dbMask;
1580
+ $lHash2 = substr($db, 0, $this->hLen);
1581
+ $m = substr($db, $this->hLen);
1582
+ if ($lHash != $lHash2) {
1583
+ user_error('Decryption error', E_USER_NOTICE);
1584
+ return false;
1585
+ }
1586
+ $m = ltrim($m, chr(0));
1587
+ if (ord($m[0]) != 1) {
1588
+ user_error('Decryption error', E_USER_NOTICE);
1589
+ return false;
1590
+ }
1591
+
1592
+ // Output the message M
1593
+
1594
+ return substr($m, 1);
1595
+ }
1596
+
1597
+ /**
1598
+ * RSAES-PKCS1-V1_5-ENCRYPT
1599
+ *
1600
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
1601
+ *
1602
+ * @access private
1603
+ * @param String $m
1604
+ * @return String
1605
+ */
1606
+ function _rsaes_pkcs1_v1_5_encrypt($m)
1607
+ {
1608
+ $mLen = strlen($m);
1609
+
1610
+ // Length checking
1611
+
1612
+ if ($mLen > $this->k - 11) {
1613
+ user_error('Message too long', E_USER_NOTICE);
1614
+ return false;
1615
+ }
1616
+
1617
+ // EME-PKCS1-v1_5 encoding
1618
+
1619
+ $ps = $this->_random($this->k - $mLen - 3, true);
1620
+ $em = chr(0) . chr(2) . $ps . chr(0) . $m;
1621
+
1622
+ // RSA encryption
1623
+ $m = $this->_os2ip($em);
1624
+ $c = $this->_rsaep($m);
1625
+ $c = $this->_i2osp($c, $this->k);
1626
+
1627
+ // Output the ciphertext C
1628
+
1629
+ return $c;
1630
+ }
1631
+
1632
+ /**
1633
+ * RSAES-PKCS1-V1_5-DECRYPT
1634
+ *
1635
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
1636
+ *
1637
+ * For compatability purposes, this function departs slightly from the description given in RFC3447.
1638
+ * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
1639
+ * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
1640
+ * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
1641
+ * to be 2 regardless of which key is used. for compatability purposes, we'll just check to make sure the
1642
+ * second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
1643
+ *
1644
+ * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
1645
+ * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
1646
+ * not private key encrypted ciphertext's.
1647
+ *
1648
+ * @access private
1649
+ * @param String $c
1650
+ * @return String
1651
+ */
1652
+ function _rsaes_pkcs1_v1_5_decrypt($c)
1653
+ {
1654
+ // Length checking
1655
+
1656
+ if (strlen($c) != $this->k) { // or if k < 11
1657
+ user_error('Decryption error', E_USER_NOTICE);
1658
+ return false;
1659
+ }
1660
+
1661
+ // RSA decryption
1662
+
1663
+ $c = $this->_os2ip($c);
1664
+ $m = $this->_rsadp($c);
1665
+ if ($m === false) {
1666
+ user_error('Decryption error', E_USER_NOTICE);
1667
+ return false;
1668
+ }
1669
+ $em = $this->_i2osp($m, $this->k);
1670
+
1671
+ // EME-PKCS1-v1_5 decoding
1672
+
1673
+ if (ord($em[0]) != 0 || ord($em[1]) > 2) {
1674
+ user_error('Decryption error', E_USER_NOTICE);
1675
+ return false;
1676
+ }
1677
+
1678
+ $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
1679
+ $m = substr($em, strlen($ps) + 3);
1680
+
1681
+ if (strlen($ps) < 8) {
1682
+ user_error('Decryption error', E_USER_NOTICE);
1683
+ return false;
1684
+ }
1685
+
1686
+ // Output M
1687
+
1688
+ return $m;
1689
+ }
1690
+
1691
+ /**
1692
+ * EMSA-PSS-ENCODE
1693
+ *
1694
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
1695
+ *
1696
+ * @access private
1697
+ * @param String $m
1698
+ * @param Integer $emBits
1699
+ */
1700
+ function _emsa_pss_encode($m, $emBits)
1701
+ {
1702
+ // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1703
+ // be output.
1704
+
1705
+ $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
1706
+ $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
1707
+
1708
+ $mHash = $this->hash->hash($m);
1709
+ if ($emLen < $this->hLen + $sLen + 2) {
1710
+ user_error('Encoding error', E_USER_NOTICE);
1711
+ return false;
1712
+ }
1713
+
1714
+ $salt = $this->_random($sLen);
1715
+ $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
1716
+ $h = $this->hash->hash($m2);
1717
+ $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
1718
+ $db = $ps . chr(1) . $salt;
1719
+ $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1720
+ $maskedDB = $db ^ $dbMask;
1721
+ $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
1722
+ $em = $maskedDB . $h . chr(0xBC);
1723
+
1724
+ return $em;
1725
+ }
1726
+
1727
+ /**
1728
+ * EMSA-PSS-VERIFY
1729
+ *
1730
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
1731
+ *
1732
+ * @access private
1733
+ * @param String $m
1734
+ * @param String $em
1735
+ * @param Integer $emBits
1736
+ * @return String
1737
+ */
1738
+ function _emsa_pss_verify($m, $em, $emBits)
1739
+ {
1740
+ // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
1741
+ // be output.
1742
+
1743
+ $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
1744
+ $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
1745
+
1746
+ $mHash = $this->hash->hash($m);
1747
+ if ($emLen < $this->hLen + $sLen + 2) {
1748
+ return false;
1749
+ }
1750
+
1751
+ if ($em[strlen($em) - 1] != chr(0xBC)) {
1752
+ return false;
1753
+ }
1754
+
1755
+ $maskedDB = substr($em, 0, $em - $this->hLen - 1);
1756
+ $h = substr($em, $em - $this->hLen - 1, $this->hLen);
1757
+ $temp = chr(0xFF << ($emBits & 7));
1758
+ if ((~$maskedDB[0] & $temp) != $temp) {
1759
+ return false;
1760
+ }
1761
+ $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
1762
+ $db = $maskedDB ^ $dbMask;
1763
+ $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
1764
+ $temp = $emLen - $this->hLen - $sLen - 2;
1765
+ if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
1766
+ return false;
1767
+ }
1768
+ $salt = substr($db, $temp + 1); // should be $sLen long
1769
+ $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
1770
+ $h2 = $this->hash->hash($m2);
1771
+ return $h == $h2;
1772
+ }
1773
+
1774
+ /**
1775
+ * RSASSA-PSS-SIGN
1776
+ *
1777
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
1778
+ *
1779
+ * @access private
1780
+ * @param String $m
1781
+ * @return String
1782
+ */
1783
+ function _rsassa_pss_sign($m)
1784
+ {
1785
+ // EMSA-PSS encoding
1786
+
1787
+ $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
1788
+
1789
+ // RSA signature
1790
+
1791
+ $m = $this->_os2ip($em);
1792
+ $s = $this->_rsasp1($m);
1793
+ $s = $this->_i2osp($s, $this->k);
1794
+
1795
+ // Output the signature S
1796
+
1797
+ return $s;
1798
+ }
1799
+
1800
+ /**
1801
+ * RSASSA-PSS-VERIFY
1802
+ *
1803
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
1804
+ *
1805
+ * @access private
1806
+ * @param String $m
1807
+ * @param String $s
1808
+ * @return String
1809
+ */
1810
+ function _rsassa_pss_verify($m, $s)
1811
+ {
1812
+ // Length checking
1813
+
1814
+ if (strlen($s) != $this->k) {
1815
+ user_error('Invalid signature', E_USER_NOTICE);
1816
+ return false;
1817
+ }
1818
+
1819
+ // RSA verification
1820
+
1821
+ $modBits = 8 * $this->k;
1822
+
1823
+ $s2 = $this->_os2ip($s);
1824
+ $m2 = $this->_rsavp1($s2);
1825
+ if ($m2 === false) {
1826
+ user_error('Invalid signature', E_USER_NOTICE);
1827
+ return false;
1828
+ }
1829
+ $em = $this->_i2osp($m2, $modBits >> 3);
1830
+ if ($em === false) {
1831
+ user_error('Invalid signature', E_USER_NOTICE);
1832
+ return false;
1833
+ }
1834
+
1835
+ // EMSA-PSS verification
1836
+
1837
+ return $this->_emsa_pss_verify($m, $em, $modBits - 1);
1838
+ }
1839
+
1840
+ /**
1841
+ * EMSA-PKCS1-V1_5-ENCODE
1842
+ *
1843
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
1844
+ *
1845
+ * @access private
1846
+ * @param String $m
1847
+ * @param Integer $emLen
1848
+ * @return String
1849
+ */
1850
+ function _emsa_pkcs1_v1_5_encode($m, $emLen)
1851
+ {
1852
+ $h = $this->hash->hash($m);
1853
+ if ($h === false) {
1854
+ return false;
1855
+ }
1856
+
1857
+ // see http://tools.ietf.org/html/rfc3447#page-43
1858
+ switch ($this->hashName) {
1859
+ case 'md2':
1860
+ $t = pack('H*', '3020300c06082a864886f70d020205000410');
1861
+ break;
1862
+ case 'md5':
1863
+ $t = pack('H*', '3020300c06082a864886f70d020505000410');
1864
+ break;
1865
+ case 'sha1':
1866
+ $t = pack('H*', '3021300906052b0e03021a05000414');
1867
+ break;
1868
+ case 'sha256':
1869
+ $t = pack('H*', '3031300d060960864801650304020105000420');
1870
+ break;
1871
+ case 'sha384':
1872
+ $t = pack('H*', '3041300d060960864801650304020205000430');
1873
+ break;
1874
+ case 'sha512':
1875
+ $t = pack('H*', '3051300d060960864801650304020305000440');
1876
+ }
1877
+ $t.= $h;
1878
+ $tLen = strlen($t);
1879
+
1880
+ if ($emLen < $tLen + 11) {
1881
+ user_error('Intended encoded message length too short', E_USER_NOTICE);
1882
+ return false;
1883
+ }
1884
+
1885
+ $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
1886
+
1887
+ $em = "\0\1$ps\0$t";
1888
+
1889
+ return $em;
1890
+ }
1891
+
1892
+ /**
1893
+ * RSASSA-PKCS1-V1_5-SIGN
1894
+ *
1895
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
1896
+ *
1897
+ * @access private
1898
+ * @param String $m
1899
+ * @return String
1900
+ */
1901
+ function _rsassa_pkcs1_v1_5_sign($m)
1902
+ {
1903
+ // EMSA-PKCS1-v1_5 encoding
1904
+
1905
+ $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
1906
+ if ($em === false) {
1907
+ user_error('RSA modulus too short', E_USER_NOTICE);
1908
+ return false;
1909
+ }
1910
+
1911
+ // RSA signature
1912
+
1913
+ $m = $this->_os2ip($em);
1914
+ $s = $this->_rsasp1($m);
1915
+ $s = $this->_i2osp($s, $this->k);
1916
+
1917
+ // Output the signature S
1918
+
1919
+ return $s;
1920
+ }
1921
+
1922
+ /**
1923
+ * RSASSA-PKCS1-V1_5-VERIFY
1924
+ *
1925
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
1926
+ *
1927
+ * @access private
1928
+ * @param String $m
1929
+ * @return String
1930
+ */
1931
+ function _rsassa_pkcs1_v1_5_verify($m, $s)
1932
+ {
1933
+ // Length checking
1934
+
1935
+ if (strlen($s) != $this->k) {
1936
+ user_error('Invalid signature', E_USER_NOTICE);
1937
+ return false;
1938
+ }
1939
+
1940
+ // RSA verification
1941
+
1942
+ $s = $this->_os2ip($s);
1943
+ $m2 = $this->_rsavp1($s);
1944
+ if ($m2 === false) {
1945
+ user_error('Invalid signature', E_USER_NOTICE);
1946
+ return false;
1947
+ }
1948
+ $em = $this->_i2osp($m2, $this->k);
1949
+ if ($em === false) {
1950
+ user_error('Invalid signature', E_USER_NOTICE);
1951
+ return false;
1952
+ }
1953
+
1954
+ // EMSA-PKCS1-v1_5 encoding
1955
+
1956
+ $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
1957
+ if ($em2 === false) {
1958
+ user_error('RSA modulus too short', E_USER_NOTICE);
1959
+ return false;
1960
+ }
1961
+
1962
+ // Compare
1963
+
1964
+ return $em === $em2;
1965
+ }
1966
+
1967
+ /**
1968
+ * Set Encryption Mode
1969
+ *
1970
+ * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
1971
+ *
1972
+ * @access public
1973
+ * @param Integer $mode
1974
+ */
1975
+ function setEncryptionMode($mode)
1976
+ {
1977
+ $this->encryptionMode = $mode;
1978
+ }
1979
+
1980
+ /**
1981
+ * Set Signature Mode
1982
+ *
1983
+ * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
1984
+ *
1985
+ * @access public
1986
+ * @param Integer $mode
1987
+ */
1988
+ function setSignatureMode($mode)
1989
+ {
1990
+ $this->signatureMode = $mode;
1991
+ }
1992
+
1993
+ /**
1994
+ * Encryption
1995
+ *
1996
+ * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
1997
+ * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
1998
+ * be concatenated together.
1999
+ *
2000
+ * @see decrypt()
2001
+ * @access public
2002
+ * @param String $plaintext
2003
+ * @return String
2004
+ */
2005
+ function encrypt($plaintext)
2006
+ {
2007
+ switch ($this->encryptionMode) {
2008
+ case CRYPT_RSA_ENCRYPTION_PKCS1:
2009
+ $length = $this->k - 11;
2010
+ if ($length <= 0) {
2011
+ return false;
2012
+ }
2013
+
2014
+ $plaintext = str_split($plaintext, $length);
2015
+ $ciphertext = '';
2016
+ foreach ($plaintext as $m) {
2017
+ $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
2018
+ }
2019
+ return $ciphertext;
2020
+ //case CRYPT_RSA_ENCRYPTION_OAEP:
2021
+ default:
2022
+ $length = $this->k - 2 * $this->hLen - 2;
2023
+ if ($length <= 0) {
2024
+ return false;
2025
+ }
2026
+
2027
+ $plaintext = str_split($plaintext, $length);
2028
+ $ciphertext = '';
2029
+ foreach ($plaintext as $m) {
2030
+ $ciphertext.= $this->_rsaes_oaep_encrypt($m);
2031
+ }
2032
+ return $ciphertext;
2033
+ }
2034
+ }
2035
+
2036
+ /**
2037
+ * Decryption
2038
+ *
2039
+ * @see encrypt()
2040
+ * @access public
2041
+ * @param String $plaintext
2042
+ * @return String
2043
+ */
2044
+ function decrypt($ciphertext)
2045
+ {
2046
+ if ($this->k <= 0) {
2047
+ return false;
2048
+ }
2049
+
2050
+ $ciphertext = str_split($ciphertext, $this->k);
2051
+ $plaintext = '';
2052
+
2053
+ switch ($this->encryptionMode) {
2054
+ case CRYPT_RSA_ENCRYPTION_PKCS1:
2055
+ $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
2056
+ break;
2057
+ //case CRYPT_RSA_ENCRYPTION_OAEP:
2058
+ default:
2059
+ $decrypt = '_rsaes_oaep_decrypt';
2060
+ }
2061
+
2062
+ foreach ($ciphertext as $c) {
2063
+ $temp = $this->$decrypt($c);
2064
+ if ($temp === false) {
2065
+ return false;
2066
+ }
2067
+ $plaintext.= $temp;
2068
+ }
2069
+
2070
+ return $plaintext;
2071
+ }
2072
+
2073
+ /**
2074
+ * Create a signature
2075
+ *
2076
+ * @see verify()
2077
+ * @access public
2078
+ * @param String $message
2079
+ * @return String
2080
+ */
2081
+ function sign($message)
2082
+ {
2083
+ if (empty($this->modulus) || empty($this->exponent)) {
2084
+ return false;
2085
+ }
2086
+
2087
+ switch ($this->signatureMode) {
2088
+ case CRYPT_RSA_SIGNATURE_PKCS1:
2089
+ return $this->_rsassa_pkcs1_v1_5_sign($message);
2090
+ //case CRYPT_RSA_SIGNATURE_PSS:
2091
+ default:
2092
+ return $this->_rsassa_pss_sign($message);
2093
+ }
2094
+ }
2095
+
2096
+ /**
2097
+ * Verifies a signature
2098
+ *
2099
+ * @see sign()
2100
+ * @access public
2101
+ * @param String $message
2102
+ * @param String $signature
2103
+ * @return Boolean
2104
+ */
2105
+ function verify($message, $signature)
2106
+ {
2107
+ if (empty($this->modulus) || empty($this->exponent)) {
2108
+ return false;
2109
+ }
2110
+
2111
+ switch ($this->signatureMode) {
2112
+ case CRYPT_RSA_SIGNATURE_PKCS1:
2113
+ return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
2114
+ //case CRYPT_RSA_SIGNATURE_PSS:
2115
+ default:
2116
+ return $this->_rsassa_pss_verify($message, $signature);
2117
+ }
2118
+ }
2119
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/Random.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Random Number Generator
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Here's a short example of how to use this library:
10
+ * <code>
11
+ * <?php
12
+ * include('Crypt/Random.php');
13
+ *
14
+ * echo crypt_random();
15
+ * ?>
16
+ * </code>
17
+ *
18
+ * LICENSE: This library is free software; you can redistribute it and/or
19
+ * modify it under the terms of the GNU Lesser General Public
20
+ * License as published by the Free Software Foundation; either
21
+ * version 2.1 of the License, or (at your option) any later version.
22
+ *
23
+ * This library is distributed in the hope that it will be useful,
24
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26
+ * Lesser General Public License for more details.
27
+ *
28
+ * You should have received a copy of the GNU Lesser General Public
29
+ * License along with this library; if not, write to the Free Software
30
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
31
+ * MA 02111-1307 USA
32
+ *
33
+ * @category Crypt
34
+ * @package Crypt_Random
35
+ * @author Jim Wigginton <terrafrost@php.net>
36
+ * @copyright MMVII Jim Wigginton
37
+ * @license http://www.gnu.org/licenses/lgpl.txt
38
+ * @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $
39
+ * @link http://phpseclib.sourceforge.net
40
+ */
41
+
42
+ /**
43
+ * Generate a random value.
44
+ *
45
+ * On 32-bit machines, the largest distance that can exist between $min and $max is 2**31.
46
+ * If $min and $max are farther apart than that then the last ($max - range) numbers.
47
+ *
48
+ * Depending on how this is being used, it may be worth while to write a replacement. For example,
49
+ * a PHP-based web app that stores its data in an SQL database can collect more entropy than this function
50
+ * can.
51
+ *
52
+ * @param optional Integer $min
53
+ * @param optional Integer $max
54
+ * @return Integer
55
+ * @access public
56
+ */
57
+ function crypt_random($min = 0, $max = 0x7FFFFFFF)
58
+ {
59
+ if ($min == $max) {
60
+ return $min;
61
+ }
62
+
63
+ // see http://en.wikipedia.org/wiki//dev/random
64
+ // if open_basedir is enabled file_exists() will ouput an "open_basedir restriction in effect" warning,
65
+ // so we suppress it.
66
+ if (@file_exists('/dev/urandom')) {
67
+ static $fp;
68
+ if (!$fp) {
69
+ $fp = fopen('/dev/urandom', 'rb');
70
+ }
71
+ extract(unpack('Nrandom', fread($fp, 4)));
72
+
73
+ // say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
74
+ // -4 % 3 + 0 = -1, even though -1 < $min
75
+ return abs($random) % ($max - $min) + $min;
76
+ }
77
+
78
+ /* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
79
+ Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
80
+
81
+ http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
82
+
83
+ The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
84
+
85
+ http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3_2/ext/standard/php_rand.h?view=markup */
86
+ if (version_compare(PHP_VERSION, '5.2.5', '<=')) {
87
+ static $seeded;
88
+ if (!isset($seeded)) {
89
+ $seeded = true;
90
+ mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
91
+ }
92
+ }
93
+
94
+ static $crypto;
95
+
96
+ // The CSPRNG's Yarrow and Fortuna periodically reseed. This function can be reseeded by hitting F5
97
+ // in the browser and reloading the page.
98
+
99
+ if (!isset($crypto)) {
100
+ $key = $iv = '';
101
+ for ($i = 0; $i < 8; $i++) {
102
+ $key.= pack('n', mt_rand(0, 0xFFFF));
103
+ $iv .= pack('n', mt_rand(0, 0xFFFF));
104
+ }
105
+ switch (true) {
106
+ case class_exists('Crypt_AES'):
107
+ $crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
108
+ break;
109
+ case class_exists('Crypt_TripleDES'):
110
+ $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
111
+ break;
112
+ case class_exists('Crypt_DES'):
113
+ $crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
114
+ break;
115
+ case class_exists('Crypt_RC4'):
116
+ $crypto = new Crypt_RC4();
117
+ break;
118
+ default:
119
+ extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF)))));
120
+ return abs($random) % ($max - $min) + $min;
121
+ }
122
+ $crypto->setKey($key);
123
+ $crypto->setIV($iv);
124
+ $crypto->enableContinuousBuffer();
125
+ }
126
+
127
+ extract(unpack('Nrandom', $crypto->encrypt("\0\0\0\0")));
128
+ return abs($random) % ($max - $min) + $min;
129
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/Rijndael.php ADDED
@@ -0,0 +1,1242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of Rijndael.
6
+ *
7
+ * Does not use mcrypt, even when available, for reasons that are explained below.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * If {@link Crypt_Rijndael::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If
12
+ * {@link Crypt_Rijndael::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
13
+ * {@link Crypt_Rijndael::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's
14
+ * 136-bits it'll be null-padded to 160-bits and 160 bits will be the key length until
15
+ * {@link Crypt_Rijndael::setKey() setKey()} is called, again, at which point, it'll be recalculated.
16
+ *
17
+ * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example,
18
+ * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
19
+ * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
20
+ * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224
21
+ * are first defined as valid key / block lengths in
22
+ * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
23
+ * Extensions: Other block and Cipher Key lengths.
24
+ *
25
+ * {@internal The variable names are the same as those in
26
+ * {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
27
+ *
28
+ * Here's a short example of how to use this library:
29
+ * <code>
30
+ * <?php
31
+ * include('Crypt/Rijndael.php');
32
+ *
33
+ * $rijndael = new Crypt_Rijndael();
34
+ *
35
+ * $rijndael->setKey('abcdefghijklmnop');
36
+ *
37
+ * $size = 10 * 1024;
38
+ * $plaintext = '';
39
+ * for ($i = 0; $i < $size; $i++) {
40
+ * $plaintext.= 'a';
41
+ * }
42
+ *
43
+ * echo $rijndael->decrypt($rijndael->encrypt($plaintext));
44
+ * ?>
45
+ * </code>
46
+ *
47
+ * LICENSE: This library is free software; you can redistribute it and/or
48
+ * modify it under the terms of the GNU Lesser General Public
49
+ * License as published by the Free Software Foundation; either
50
+ * version 2.1 of the License, or (at your option) any later version.
51
+ *
52
+ * This library is distributed in the hope that it will be useful,
53
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
54
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
55
+ * Lesser General Public License for more details.
56
+ *
57
+ * You should have received a copy of the GNU Lesser General Public
58
+ * License along with this library; if not, write to the Free Software
59
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
60
+ * MA 02111-1307 USA
61
+ *
62
+ * @category Crypt
63
+ * @package Crypt_Rijndael
64
+ * @author Jim Wigginton <terrafrost@php.net>
65
+ * @copyright MMVIII Jim Wigginton
66
+ * @license http://www.gnu.org/licenses/lgpl.txt
67
+ * @version $Id: Rijndael.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
68
+ * @link http://phpseclib.sourceforge.net
69
+ */
70
+
71
+ /**#@+
72
+ * @access public
73
+ * @see Crypt_Rijndael::encrypt()
74
+ * @see Crypt_Rijndael::decrypt()
75
+ */
76
+ /**
77
+ * Encrypt / decrypt using the Counter mode.
78
+ *
79
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
80
+ *
81
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
82
+ */
83
+ define('CRYPT_RIJNDAEL_MODE_CTR', -1);
84
+ /**
85
+ * Encrypt / decrypt using the Electronic Code Book mode.
86
+ *
87
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
88
+ */
89
+ define('CRYPT_RIJNDAEL_MODE_ECB', 1);
90
+ /**
91
+ * Encrypt / decrypt using the Code Book Chaining mode.
92
+ *
93
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
94
+ */
95
+ define('CRYPT_RIJNDAEL_MODE_CBC', 2);
96
+ /**#@-*/
97
+
98
+ /**#@+
99
+ * @access private
100
+ * @see Crypt_Rijndael::Crypt_Rijndael()
101
+ */
102
+ /**
103
+ * Toggles the internal implementation
104
+ */
105
+ define('CRYPT_RIJNDAEL_MODE_INTERNAL', 1);
106
+ /**
107
+ * Toggles the mcrypt implementation
108
+ */
109
+ define('CRYPT_RIJNDAEL_MODE_MCRYPT', 2);
110
+ /**#@-*/
111
+
112
+ /**
113
+ * Pure-PHP implementation of Rijndael.
114
+ *
115
+ * @author Jim Wigginton <terrafrost@php.net>
116
+ * @version 0.1.0
117
+ * @access public
118
+ * @package Crypt_Rijndael
119
+ */
120
+ class Crypt_Rijndael {
121
+ /**
122
+ * The Encryption Mode
123
+ *
124
+ * @see Crypt_Rijndael::Crypt_Rijndael()
125
+ * @var Integer
126
+ * @access private
127
+ */
128
+ var $mode;
129
+
130
+ /**
131
+ * The Key
132
+ *
133
+ * @see Crypt_Rijndael::setKey()
134
+ * @var String
135
+ * @access private
136
+ */
137
+ var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
138
+
139
+ /**
140
+ * The Initialization Vector
141
+ *
142
+ * @see Crypt_Rijndael::setIV()
143
+ * @var String
144
+ * @access private
145
+ */
146
+ var $iv = '';
147
+
148
+ /**
149
+ * A "sliding" Initialization Vector
150
+ *
151
+ * @see Crypt_Rijndael::enableContinuousBuffer()
152
+ * @var String
153
+ * @access private
154
+ */
155
+ var $encryptIV = '';
156
+
157
+ /**
158
+ * A "sliding" Initialization Vector
159
+ *
160
+ * @see Crypt_Rijndael::enableContinuousBuffer()
161
+ * @var String
162
+ * @access private
163
+ */
164
+ var $decryptIV = '';
165
+
166
+ /**
167
+ * Continuous Buffer status
168
+ *
169
+ * @see Crypt_Rijndael::enableContinuousBuffer()
170
+ * @var Boolean
171
+ * @access private
172
+ */
173
+ var $continuousBuffer = false;
174
+
175
+ /**
176
+ * Padding status
177
+ *
178
+ * @see Crypt_Rijndael::enablePadding()
179
+ * @var Boolean
180
+ * @access private
181
+ */
182
+ var $padding = true;
183
+
184
+ /**
185
+ * Does the key schedule need to be (re)calculated?
186
+ *
187
+ * @see setKey()
188
+ * @see setBlockLength()
189
+ * @see setKeyLength()
190
+ * @var Boolean
191
+ * @access private
192
+ */
193
+ var $changed = true;
194
+
195
+ /**
196
+ * Has the key length explicitly been set or should it be derived from the key, itself?
197
+ *
198
+ * @see setKeyLength()
199
+ * @var Boolean
200
+ * @access private
201
+ */
202
+ var $explicit_key_length = false;
203
+
204
+ /**
205
+ * The Key Schedule
206
+ *
207
+ * @see _setup()
208
+ * @var Array
209
+ * @access private
210
+ */
211
+ var $w;
212
+
213
+ /**
214
+ * The Inverse Key Schedule
215
+ *
216
+ * @see _setup()
217
+ * @var Array
218
+ * @access private
219
+ */
220
+ var $dw;
221
+
222
+ /**
223
+ * The Block Length
224
+ *
225
+ * @see setBlockLength()
226
+ * @var Integer
227
+ * @access private
228
+ * @internal The max value is 32, the min value is 16. All valid values are multiples of 4. Exists in conjunction with
229
+ * $Nb because we need this value and not $Nb to pad strings appropriately.
230
+ */
231
+ var $block_size = 16;
232
+
233
+ /**
234
+ * The Block Length divided by 32
235
+ *
236
+ * @see setBlockLength()
237
+ * @var Integer
238
+ * @access private
239
+ * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
240
+ * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
241
+ * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
242
+ * of that, we'll just precompute it once.
243
+ *
244
+ */
245
+ var $Nb = 4;
246
+
247
+ /**
248
+ * The Key Length
249
+ *
250
+ * @see setKeyLength()
251
+ * @var Integer
252
+ * @access private
253
+ * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $key_size
254
+ * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could
255
+ * derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
256
+ * of that, we'll just precompute it once.
257
+ */
258
+ var $key_size = 16;
259
+
260
+ /**
261
+ * The Key Length divided by 32
262
+ *
263
+ * @see setKeyLength()
264
+ * @var Integer
265
+ * @access private
266
+ * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
267
+ */
268
+ var $Nk = 4;
269
+
270
+ /**
271
+ * The Number of Rounds
272
+ *
273
+ * @var Integer
274
+ * @access private
275
+ * @internal The max value is 14, the min value is 10.
276
+ */
277
+ var $Nr;
278
+
279
+ /**
280
+ * Shift offsets
281
+ *
282
+ * @var Array
283
+ * @access private
284
+ */
285
+ var $c;
286
+
287
+ /**
288
+ * Precomputed mixColumns table
289
+ *
290
+ * @see Crypt_Rijndael()
291
+ * @var Array
292
+ * @access private
293
+ */
294
+ var $t0;
295
+
296
+ /**
297
+ * Precomputed mixColumns table
298
+ *
299
+ * @see Crypt_Rijndael()
300
+ * @var Array
301
+ * @access private
302
+ */
303
+ var $t1;
304
+
305
+ /**
306
+ * Precomputed mixColumns table
307
+ *
308
+ * @see Crypt_Rijndael()
309
+ * @var Array
310
+ * @access private
311
+ */
312
+ var $t2;
313
+
314
+ /**
315
+ * Precomputed mixColumns table
316
+ *
317
+ * @see Crypt_Rijndael()
318
+ * @var Array
319
+ * @access private
320
+ */
321
+ var $t3;
322
+
323
+ /**
324
+ * Precomputed invMixColumns table
325
+ *
326
+ * @see Crypt_Rijndael()
327
+ * @var Array
328
+ * @access private
329
+ */
330
+ var $dt0;
331
+
332
+ /**
333
+ * Precomputed invMixColumns table
334
+ *
335
+ * @see Crypt_Rijndael()
336
+ * @var Array
337
+ * @access private
338
+ */
339
+ var $dt1;
340
+
341
+ /**
342
+ * Precomputed invMixColumns table
343
+ *
344
+ * @see Crypt_Rijndael()
345
+ * @var Array
346
+ * @access private
347
+ */
348
+ var $dt2;
349
+
350
+ /**
351
+ * Precomputed invMixColumns table
352
+ *
353
+ * @see Crypt_Rijndael()
354
+ * @var Array
355
+ * @access private
356
+ */
357
+ var $dt3;
358
+
359
+ /**
360
+ * Default Constructor.
361
+ *
362
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
363
+ * CRYPT_RIJNDAEL_MODE_ECB or CRYPT_RIJNDAEL_MODE_CBC. If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used.
364
+ *
365
+ * @param optional Integer $mode
366
+ * @return Crypt_Rijndael
367
+ * @access public
368
+ */
369
+ function Crypt_Rijndael($mode = CRYPT_RIJNDAEL_MODE_CBC)
370
+ {
371
+ switch ($mode) {
372
+ case CRYPT_RIJNDAEL_MODE_ECB:
373
+ case CRYPT_RIJNDAEL_MODE_CBC:
374
+ case CRYPT_RIJNDAEL_MODE_CTR:
375
+ $this->mode = $mode;
376
+ break;
377
+ default:
378
+ $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
379
+ }
380
+
381
+ $t3 = &$this->t3;
382
+ $t2 = &$this->t2;
383
+ $t1 = &$this->t1;
384
+ $t0 = &$this->t0;
385
+
386
+ $dt3 = &$this->dt3;
387
+ $dt2 = &$this->dt2;
388
+ $dt1 = &$this->dt1;
389
+ $dt0 = &$this->dt0;
390
+
391
+ // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
392
+ // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
393
+ // those are the names we'll use.
394
+ $t3 = array(
395
+ 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
396
+ 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
397
+ 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
398
+ 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
399
+ 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
400
+ 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
401
+ 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
402
+ 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
403
+ 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
404
+ 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
405
+ 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
406
+ 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
407
+ 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
408
+ 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
409
+ 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
410
+ 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
411
+ 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
412
+ 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
413
+ 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
414
+ 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
415
+ 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
416
+ 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
417
+ 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
418
+ 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
419
+ 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
420
+ 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
421
+ 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
422
+ 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
423
+ 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
424
+ 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
425
+ 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
426
+ 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
427
+ );
428
+
429
+ $dt3 = array(
430
+ 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
431
+ 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
432
+ 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
433
+ 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
434
+ 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
435
+ 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
436
+ 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
437
+ 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
438
+ 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
439
+ 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
440
+ 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
441
+ 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
442
+ 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
443
+ 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
444
+ 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
445
+ 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
446
+ 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
447
+ 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
448
+ 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
449
+ 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
450
+ 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
451
+ 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
452
+ 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
453
+ 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
454
+ 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
455
+ 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
456
+ 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
457
+ 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
458
+ 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
459
+ 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
460
+ 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
461
+ 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
462
+ );
463
+
464
+ for ($i = 0; $i < 256; $i++) {
465
+ $t2[$i << 8] = (($t3[$i] << 8) & 0xFFFFFF00) | (($t3[$i] >> 24) & 0x000000FF);
466
+ $t1[$i << 16] = (($t3[$i] << 16) & 0xFFFF0000) | (($t3[$i] >> 16) & 0x0000FFFF);
467
+ $t0[$i << 24] = (($t3[$i] << 24) & 0xFF000000) | (($t3[$i] >> 8) & 0x00FFFFFF);
468
+
469
+ $dt2[$i << 8] = (($this->dt3[$i] << 8) & 0xFFFFFF00) | (($dt3[$i] >> 24) & 0x000000FF);
470
+ $dt1[$i << 16] = (($this->dt3[$i] << 16) & 0xFFFF0000) | (($dt3[$i] >> 16) & 0x0000FFFF);
471
+ $dt0[$i << 24] = (($this->dt3[$i] << 24) & 0xFF000000) | (($dt3[$i] >> 8) & 0x00FFFFFF);
472
+ }
473
+ }
474
+
475
+ /**
476
+ * Sets the key.
477
+ *
478
+ * Keys can be of any length. Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and
479
+ * whose length is a multiple of 32. If the key is less than 256-bits and the key length isn't set, we round the length
480
+ * up to the closest valid key length, padding $key with null bytes. If the key is more than 256-bits, we trim the
481
+ * excess bits.
482
+ *
483
+ * If the key is not explicitly set, it'll be assumed to be all null bytes.
484
+ *
485
+ * @access public
486
+ * @param String $key
487
+ */
488
+ function setKey($key)
489
+ {
490
+ $this->key = $key;
491
+ $this->changed = true;
492
+ }
493
+
494
+ /**
495
+ * Sets the initialization vector. (optional)
496
+ *
497
+ * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed
498
+ * to be all zero's.
499
+ *
500
+ * @access public
501
+ * @param String $iv
502
+ */
503
+ function setIV($iv)
504
+ {
505
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, $this->block_size), $this->block_size, chr(0));;
506
+ }
507
+
508
+ /**
509
+ * Sets the key length
510
+ *
511
+ * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
512
+ * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount.
513
+ *
514
+ * @access public
515
+ * @param Integer $length
516
+ */
517
+ function setKeyLength($length)
518
+ {
519
+ $length >>= 5;
520
+ if ($length > 8) {
521
+ $length = 8;
522
+ } else if ($length < 4) {
523
+ $length = 4;
524
+ }
525
+ $this->Nk = $length;
526
+ $this->key_size = $length << 2;
527
+
528
+ $this->explicit_key_length = true;
529
+ $this->changed = true;
530
+ }
531
+
532
+ /**
533
+ * Sets the block length
534
+ *
535
+ * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
536
+ * 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount.
537
+ *
538
+ * @access public
539
+ * @param Integer $length
540
+ */
541
+ function setBlockLength($length)
542
+ {
543
+ $length >>= 5;
544
+ if ($length > 8) {
545
+ $length = 8;
546
+ } else if ($length < 4) {
547
+ $length = 4;
548
+ }
549
+ $this->Nb = $length;
550
+ $this->block_size = $length << 2;
551
+ $this->changed = true;
552
+ }
553
+
554
+ /**
555
+ * Generate CTR XOR encryption key
556
+ *
557
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
558
+ * plaintext / ciphertext in CTR mode.
559
+ *
560
+ * @see Crypt_Rijndael::decrypt()
561
+ * @see Crypt_Rijndael::encrypt()
562
+ * @access public
563
+ * @param Integer $length
564
+ * @param String $iv
565
+ */
566
+ function _generate_xor($length, &$iv)
567
+ {
568
+ $xor = '';
569
+ $block_size = $this->block_size;
570
+ $num_blocks = floor(($length + ($block_size - 1)) / $block_size);
571
+ for ($i = 0; $i < $num_blocks; $i++) {
572
+ $xor.= $iv;
573
+ for ($j = 4; $j <= $block_size; $j+=4) {
574
+ $temp = substr($iv, -$j, 4);
575
+ switch ($temp) {
576
+ case "\xFF\xFF\xFF\xFF":
577
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
578
+ break;
579
+ case "\x7F\xFF\xFF\xFF":
580
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
581
+ break 2;
582
+ default:
583
+ extract(unpack('Ncount', $temp));
584
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
585
+ break 2;
586
+ }
587
+ }
588
+ }
589
+
590
+ return $xor;
591
+ }
592
+
593
+ /**
594
+ * Encrypts a message.
595
+ *
596
+ * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other Rjindael
597
+ * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's
598
+ * necessary are discussed in the following
599
+ * URL:
600
+ *
601
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
602
+ *
603
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
604
+ * strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
605
+ * length.
606
+ *
607
+ * @see Crypt_Rijndael::decrypt()
608
+ * @access public
609
+ * @param String $plaintext
610
+ */
611
+ function encrypt($plaintext)
612
+ {
613
+ $this->_setup();
614
+ if ($this->mode != CRYPT_RIJNDAEL_MODE_CTR) {
615
+ $plaintext = $this->_pad($plaintext);
616
+ }
617
+
618
+ $block_size = $this->block_size;
619
+ $ciphertext = '';
620
+ switch ($this->mode) {
621
+ case CRYPT_RIJNDAEL_MODE_ECB:
622
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
623
+ $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
624
+ }
625
+ break;
626
+ case CRYPT_RIJNDAEL_MODE_CBC:
627
+ $xor = $this->encryptIV;
628
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
629
+ $block = substr($plaintext, $i, $block_size);
630
+ $block = $this->_encryptBlock($block ^ $xor);
631
+ $xor = $block;
632
+ $ciphertext.= $block;
633
+ }
634
+ if ($this->continuousBuffer) {
635
+ $this->encryptIV = $xor;
636
+ }
637
+ break;
638
+ case CRYPT_RIJNDAEL_MODE_CTR:
639
+ $xor = $this->encryptIV;
640
+ for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
641
+ $block = substr($plaintext, $i, $block_size);
642
+ $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
643
+ $ciphertext.= $block ^ $key;
644
+ }
645
+ if ($this->continuousBuffer) {
646
+ $this->encryptIV = $xor;
647
+ }
648
+ }
649
+
650
+ return $ciphertext;
651
+ }
652
+
653
+ /**
654
+ * Decrypts a message.
655
+ *
656
+ * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
657
+ * it is.
658
+ *
659
+ * @see Crypt_Rijndael::encrypt()
660
+ * @access public
661
+ * @param String $ciphertext
662
+ */
663
+ function decrypt($ciphertext)
664
+ {
665
+ $this->_setup();
666
+
667
+ if ($this->mode != CRYPT_RIJNDAEL_MODE_CTR) {
668
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
669
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
670
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + $this->block_size - 1) % $this->block_size, chr(0));
671
+ }
672
+
673
+ $block_size = $this->block_size;
674
+ $plaintext = '';
675
+ switch ($this->mode) {
676
+ case CRYPT_RIJNDAEL_MODE_ECB:
677
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
678
+ $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
679
+ }
680
+ break;
681
+ case CRYPT_RIJNDAEL_MODE_CBC:
682
+ $xor = $this->decryptIV;
683
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
684
+ $block = substr($ciphertext, $i, $block_size);
685
+ $plaintext.= $this->_decryptBlock($block) ^ $xor;
686
+ $xor = $block;
687
+ }
688
+ if ($this->continuousBuffer) {
689
+ $this->decryptIV = $xor;
690
+ }
691
+ break;
692
+ case CRYPT_RIJNDAEL_MODE_CTR:
693
+ $xor = $this->decryptIV;
694
+ for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
695
+ $block = substr($ciphertext, $i, $block_size);
696
+ $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
697
+ $plaintext.= $block ^ $key;
698
+ }
699
+ if ($this->continuousBuffer) {
700
+ $this->decryptIV = $xor;
701
+ }
702
+ }
703
+
704
+ return $this->mode != CRYPT_RIJNDAEL_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
705
+ }
706
+
707
+ /**
708
+ * Encrypts a block
709
+ *
710
+ * @access private
711
+ * @param String $in
712
+ * @return String
713
+ */
714
+ function _encryptBlock($in)
715
+ {
716
+ $state = array();
717
+ $words = unpack('N*word', $in);
718
+
719
+ $w = $this->w;
720
+ $t0 = $this->t0;
721
+ $t1 = $this->t1;
722
+ $t2 = $this->t2;
723
+ $t3 = $this->t3;
724
+ $Nb = $this->Nb;
725
+ $Nr = $this->Nr;
726
+ $c = $this->c;
727
+
728
+ // addRoundKey
729
+ $i = 0;
730
+ foreach ($words as $word) {
731
+ $state[] = $word ^ $w[0][$i++];
732
+ }
733
+
734
+ // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
735
+ // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
736
+ // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
737
+ // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
738
+ // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1],
739
+ // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
740
+
741
+ // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
742
+ $temp = array();
743
+ for ($round = 1; $round < $Nr; $round++) {
744
+ $i = 0; // $c[0] == 0
745
+ $j = $c[1];
746
+ $k = $c[2];
747
+ $l = $c[3];
748
+
749
+ while ($i < $this->Nb) {
750
+ $temp[$i] = $t0[$state[$i] & 0xFF000000] ^
751
+ $t1[$state[$j] & 0x00FF0000] ^
752
+ $t2[$state[$k] & 0x0000FF00] ^
753
+ $t3[$state[$l] & 0x000000FF] ^
754
+ $w[$round][$i];
755
+ $i++;
756
+ $j = ($j + 1) % $Nb;
757
+ $k = ($k + 1) % $Nb;
758
+ $l = ($l + 1) % $Nb;
759
+ }
760
+
761
+ for ($i = 0; $i < $Nb; $i++) {
762
+ $state[$i] = $temp[$i];
763
+ }
764
+ }
765
+
766
+ // subWord
767
+ for ($i = 0; $i < $Nb; $i++) {
768
+ $state[$i] = $this->_subWord($state[$i]);
769
+ }
770
+
771
+ // shiftRows + addRoundKey
772
+ $i = 0; // $c[0] == 0
773
+ $j = $c[1];
774
+ $k = $c[2];
775
+ $l = $c[3];
776
+ while ($i < $this->Nb) {
777
+ $temp[$i] = ($state[$i] & 0xFF000000) ^
778
+ ($state[$j] & 0x00FF0000) ^
779
+ ($state[$k] & 0x0000FF00) ^
780
+ ($state[$l] & 0x000000FF) ^
781
+ $w[$Nr][$i];
782
+ $i++;
783
+ $j = ($j + 1) % $Nb;
784
+ $k = ($k + 1) % $Nb;
785
+ $l = ($l + 1) % $Nb;
786
+ }
787
+ $state = $temp;
788
+
789
+ array_unshift($state, 'N*');
790
+
791
+ return call_user_func_array('pack', $state);
792
+ }
793
+
794
+ /**
795
+ * Decrypts a block
796
+ *
797
+ * @access private
798
+ * @param String $in
799
+ * @return String
800
+ */
801
+ function _decryptBlock($in)
802
+ {
803
+ $state = array();
804
+ $words = unpack('N*word', $in);
805
+
806
+ $num_states = count($state);
807
+ $dw = $this->dw;
808
+ $dt0 = $this->dt0;
809
+ $dt1 = $this->dt1;
810
+ $dt2 = $this->dt2;
811
+ $dt3 = $this->dt3;
812
+ $Nb = $this->Nb;
813
+ $Nr = $this->Nr;
814
+ $c = $this->c;
815
+
816
+ // addRoundKey
817
+ $i = 0;
818
+ foreach ($words as $word) {
819
+ $state[] = $word ^ $dw[$Nr][$i++];
820
+ }
821
+
822
+ $temp = array();
823
+ for ($round = $Nr - 1; $round > 0; $round--) {
824
+ $i = 0; // $c[0] == 0
825
+ $j = $Nb - $c[1];
826
+ $k = $Nb - $c[2];
827
+ $l = $Nb - $c[3];
828
+
829
+ while ($i < $Nb) {
830
+ $temp[$i] = $dt0[$state[$i] & 0xFF000000] ^
831
+ $dt1[$state[$j] & 0x00FF0000] ^
832
+ $dt2[$state[$k] & 0x0000FF00] ^
833
+ $dt3[$state[$l] & 0x000000FF] ^
834
+ $dw[$round][$i];
835
+ $i++;
836
+ $j = ($j + 1) % $Nb;
837
+ $k = ($k + 1) % $Nb;
838
+ $l = ($l + 1) % $Nb;
839
+ }
840
+
841
+ for ($i = 0; $i < $Nb; $i++) {
842
+ $state[$i] = $temp[$i];
843
+ }
844
+ }
845
+
846
+ // invShiftRows + invSubWord + addRoundKey
847
+ $i = 0; // $c[0] == 0
848
+ $j = $Nb - $c[1];
849
+ $k = $Nb - $c[2];
850
+ $l = $Nb - $c[3];
851
+
852
+ while ($i < $Nb) {
853
+ $temp[$i] = $dw[0][$i] ^
854
+ $this->_invSubWord(($state[$i] & 0xFF000000) |
855
+ ($state[$j] & 0x00FF0000) |
856
+ ($state[$k] & 0x0000FF00) |
857
+ ($state[$l] & 0x000000FF));
858
+ $i++;
859
+ $j = ($j + 1) % $Nb;
860
+ $k = ($k + 1) % $Nb;
861
+ $l = ($l + 1) % $Nb;
862
+ }
863
+
864
+ $state = $temp;
865
+
866
+ array_unshift($state, 'N*');
867
+
868
+ return call_user_func_array('pack', $state);
869
+ }
870
+
871
+ /**
872
+ * Setup Rijndael
873
+ *
874
+ * Validates all the variables and calculates $Nr - the number of rounds that need to be performed - and $w - the key
875
+ * key schedule.
876
+ *
877
+ * @access private
878
+ */
879
+ function _setup()
880
+ {
881
+ // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
882
+ // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
883
+ static $rcon = array(0,
884
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
885
+ 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
886
+ 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
887
+ 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
888
+ 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
889
+ 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
890
+ );
891
+
892
+ if (!$this->changed) {
893
+ return;
894
+ }
895
+
896
+ if (!$this->explicit_key_length) {
897
+ // we do >> 2, here, and not >> 5, as we do above, since strlen($this->key) tells us the number of bytes - not bits
898
+ $length = strlen($this->key) >> 2;
899
+ if ($length > 8) {
900
+ $length = 8;
901
+ } else if ($length < 4) {
902
+ $length = 4;
903
+ }
904
+ $this->Nk = $length;
905
+ $this->key_size = $length << 2;
906
+ }
907
+
908
+ $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));
909
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, chr(0));
910
+
911
+ // see Rijndael-ammended.pdf#page=44
912
+ $this->Nr = max($this->Nk, $this->Nb) + 6;
913
+
914
+ // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
915
+ // "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
916
+ // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
917
+ // "Table 2: Shift offsets for different block lengths"
918
+ switch ($this->Nb) {
919
+ case 4:
920
+ case 5:
921
+ case 6:
922
+ $this->c = array(0, 1, 2, 3);
923
+ break;
924
+ case 7:
925
+ $this->c = array(0, 1, 2, 4);
926
+ break;
927
+ case 8:
928
+ $this->c = array(0, 1, 3, 4);
929
+ }
930
+
931
+ $key = $this->key;
932
+
933
+ $w = array_values(unpack('N*words', $key));
934
+
935
+ $length = $this->Nb * ($this->Nr + 1);
936
+ for ($i = $this->Nk; $i < $length; $i++) {
937
+ $temp = $w[$i - 1];
938
+ if ($i % $this->Nk == 0) {
939
+ // according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
940
+ // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
941
+ // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
942
+ // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
943
+ $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
944
+ $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
945
+ } else if ($this->Nk > 6 && $i % $this->Nk == 4) {
946
+ $temp = $this->_subWord($temp);
947
+ }
948
+ $w[$i] = $w[$i - $this->Nk] ^ $temp;
949
+ }
950
+
951
+ // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
952
+ // and generate the inverse key schedule. more specifically,
953
+ // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
954
+ // "The key expansion for the Inverse Cipher is defined as follows:
955
+ // 1. Apply the Key Expansion.
956
+ // 2. Apply InvMixColumn to all Round Keys except the first and the last one."
957
+ // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
958
+ $temp = array();
959
+ for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
960
+ if ($col == $this->Nb) {
961
+ if ($row == 0) {
962
+ $this->dw[0] = $this->w[0];
963
+ } else {
964
+ // subWord + invMixColumn + invSubWord = invMixColumn
965
+ $j = 0;
966
+ while ($j < $this->Nb) {
967
+ $dw = $this->_subWord($this->w[$row][$j]);
968
+ $temp[$j] = $this->dt0[$dw & 0xFF000000] ^
969
+ $this->dt1[$dw & 0x00FF0000] ^
970
+ $this->dt2[$dw & 0x0000FF00] ^
971
+ $this->dt3[$dw & 0x000000FF];
972
+ $j++;
973
+ }
974
+ $this->dw[$row] = $temp;
975
+ }
976
+
977
+ $col = 0;
978
+ $row++;
979
+ }
980
+ $this->w[$row][$col] = $w[$i];
981
+ }
982
+
983
+ $this->dw[$row] = $this->w[$row];
984
+
985
+ $this->changed = false;
986
+ }
987
+
988
+ /**
989
+ * Performs S-Box substitutions
990
+ *
991
+ * @access private
992
+ */
993
+ function _subWord($word)
994
+ {
995
+ static $sbox0, $sbox1, $sbox2, $sbox3;
996
+
997
+ if (empty($sbox0)) {
998
+ $sbox0 = array(
999
+ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
1000
+ 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
1001
+ 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
1002
+ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
1003
+ 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
1004
+ 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
1005
+ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
1006
+ 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
1007
+ 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
1008
+ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
1009
+ 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
1010
+ 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
1011
+ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
1012
+ 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
1013
+ 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
1014
+ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
1015
+ );
1016
+
1017
+ $sbox1 = array();
1018
+ $sbox2 = array();
1019
+ $sbox3 = array();
1020
+
1021
+ for ($i = 0; $i < 256; $i++) {
1022
+ $sbox1[$i << 8] = $sbox0[$i] << 8;
1023
+ $sbox2[$i << 16] = $sbox0[$i] << 16;
1024
+ $sbox3[$i << 24] = $sbox0[$i] << 24;
1025
+ }
1026
+ }
1027
+
1028
+ return $sbox0[$word & 0x000000FF] |
1029
+ $sbox1[$word & 0x0000FF00] |
1030
+ $sbox2[$word & 0x00FF0000] |
1031
+ $sbox3[$word & 0xFF000000];
1032
+ }
1033
+
1034
+ /**
1035
+ * Performs inverse S-Box substitutions
1036
+ *
1037
+ * @access private
1038
+ */
1039
+ function _invSubWord($word)
1040
+ {
1041
+ static $sbox0, $sbox1, $sbox2, $sbox3;
1042
+
1043
+ if (empty($sbox0)) {
1044
+ $sbox0 = array(
1045
+ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
1046
+ 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
1047
+ 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
1048
+ 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
1049
+ 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
1050
+ 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
1051
+ 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
1052
+ 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
1053
+ 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
1054
+ 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
1055
+ 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
1056
+ 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
1057
+ 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
1058
+ 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
1059
+ 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
1060
+ 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
1061
+ );
1062
+
1063
+ $sbox1 = array();
1064
+ $sbox2 = array();
1065
+ $sbox3 = array();
1066
+
1067
+ for ($i = 0; $i < 256; $i++) {
1068
+ $sbox1[$i << 8] = $sbox0[$i] << 8;
1069
+ $sbox2[$i << 16] = $sbox0[$i] << 16;
1070
+ $sbox3[$i << 24] = $sbox0[$i] << 24;
1071
+ }
1072
+ }
1073
+
1074
+ return $sbox0[$word & 0x000000FF] |
1075
+ $sbox1[$word & 0x0000FF00] |
1076
+ $sbox2[$word & 0x00FF0000] |
1077
+ $sbox3[$word & 0xFF000000];
1078
+ }
1079
+
1080
+ /**
1081
+ * Pad "packets".
1082
+ *
1083
+ * Rijndael works by encrypting between sixteen and thirty-two bytes at a time, provided that number is also a multiple
1084
+ * of four. If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to
1085
+ * pad the input so that it is of the proper length.
1086
+ *
1087
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH,
1088
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
1089
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
1090
+ * transmitted separately)
1091
+ *
1092
+ * @see Crypt_Rijndael::disablePadding()
1093
+ * @access public
1094
+ */
1095
+ function enablePadding()
1096
+ {
1097
+ $this->padding = true;
1098
+ }
1099
+
1100
+ /**
1101
+ * Do not pad packets.
1102
+ *
1103
+ * @see Crypt_Rijndael::enablePadding()
1104
+ * @access public
1105
+ */
1106
+ function disablePadding()
1107
+ {
1108
+ $this->padding = false;
1109
+ }
1110
+
1111
+ /**
1112
+ * Pads a string
1113
+ *
1114
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize.
1115
+ * $block_size - (strlen($text) % $block_size) bytes are added, each of which is equal to
1116
+ * chr($block_size - (strlen($text) % $block_size)
1117
+ *
1118
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
1119
+ * and padding will, hence forth, be enabled.
1120
+ *
1121
+ * @see Crypt_Rijndael::_unpad()
1122
+ * @access private
1123
+ */
1124
+ function _pad($text)
1125
+ {
1126
+ $length = strlen($text);
1127
+
1128
+ if (!$this->padding) {
1129
+ if ($length % $this->block_size == 0) {
1130
+ return $text;
1131
+ } else {
1132
+ user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})", E_USER_NOTICE);
1133
+ $this->padding = true;
1134
+ }
1135
+ }
1136
+
1137
+ $pad = $this->block_size - ($length % $this->block_size);
1138
+
1139
+ return str_pad($text, $length + $pad, chr($pad));
1140
+ }
1141
+
1142
+ /**
1143
+ * Unpads a string.
1144
+ *
1145
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
1146
+ * and false will be returned.
1147
+ *
1148
+ * @see Crypt_Rijndael::_pad()
1149
+ * @access private
1150
+ */
1151
+ function _unpad($text)
1152
+ {
1153
+ if (!$this->padding) {
1154
+ return $text;
1155
+ }
1156
+
1157
+ $length = ord($text[strlen($text) - 1]);
1158
+
1159
+ if (!$length || $length > $this->block_size) {
1160
+ return false;
1161
+ }
1162
+
1163
+ return substr($text, 0, -$length);
1164
+ }
1165
+
1166
+ /**
1167
+ * Treat consecutive "packets" as if they are a continuous buffer.
1168
+ *
1169
+ * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets
1170
+ * will yield different outputs:
1171
+ *
1172
+ * <code>
1173
+ * echo $rijndael->encrypt(substr($plaintext, 0, 16));
1174
+ * echo $rijndael->encrypt(substr($plaintext, 16, 16));
1175
+ * </code>
1176
+ * <code>
1177
+ * echo $rijndael->encrypt($plaintext);
1178
+ * </code>
1179
+ *
1180
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
1181
+ * another, as demonstrated with the following:
1182
+ *
1183
+ * <code>
1184
+ * $rijndael->encrypt(substr($plaintext, 0, 16));
1185
+ * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16)));
1186
+ * </code>
1187
+ * <code>
1188
+ * echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16)));
1189
+ * </code>
1190
+ *
1191
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
1192
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
1193
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
1194
+ *
1195
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_Rijndael() object changes after each
1196
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
1197
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
1198
+ * however, they are also less intuitive and more likely to cause you problems.
1199
+ *
1200
+ * @see Crypt_Rijndael::disableContinuousBuffer()
1201
+ * @access public
1202
+ */
1203
+ function enableContinuousBuffer()
1204
+ {
1205
+ $this->continuousBuffer = true;
1206
+ }
1207
+
1208
+ /**
1209
+ * Treat consecutive packets as if they are a discontinuous buffer.
1210
+ *
1211
+ * The default behavior.
1212
+ *
1213
+ * @see Crypt_Rijndael::enableContinuousBuffer()
1214
+ * @access public
1215
+ */
1216
+ function disableContinuousBuffer()
1217
+ {
1218
+ $this->continuousBuffer = false;
1219
+ $this->encryptIV = $this->iv;
1220
+ $this->decryptIV = $this->iv;
1221
+ }
1222
+
1223
+ /**
1224
+ * String Shift
1225
+ *
1226
+ * Inspired by array_shift
1227
+ *
1228
+ * @param String $string
1229
+ * @param optional Integer $index
1230
+ * @return String
1231
+ * @access private
1232
+ */
1233
+ function _string_shift(&$string, $index = 1)
1234
+ {
1235
+ $substr = substr($string, 0, $index);
1236
+ $string = substr($string, $index);
1237
+ return $substr;
1238
+ }
1239
+ }
1240
+
1241
+ // vim: ts=4:sw=4:et:
1242
+ // vim6: fdl=1:
app/code/local/Wisepricer/Syncer/lib/phpseclib/Crypt/TripleDES.php ADDED
@@ -0,0 +1,690 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of Triple DES.
6
+ *
7
+ * Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt).
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * Here's a short example of how to use this library:
12
+ * <code>
13
+ * <?php
14
+ * include('Crypt/TripleDES.php');
15
+ *
16
+ * $des = new Crypt_TripleDES();
17
+ *
18
+ * $des->setKey('abcdefghijklmnopqrstuvwx');
19
+ *
20
+ * $size = 10 * 1024;
21
+ * $plaintext = '';
22
+ * for ($i = 0; $i < $size; $i++) {
23
+ * $plaintext.= 'a';
24
+ * }
25
+ *
26
+ * echo $des->decrypt($des->encrypt($plaintext));
27
+ * ?>
28
+ * </code>
29
+ *
30
+ * LICENSE: This library is free software; you can redistribute it and/or
31
+ * modify it under the terms of the GNU Lesser General Public
32
+ * License as published by the Free Software Foundation; either
33
+ * version 2.1 of the License, or (at your option) any later version.
34
+ *
35
+ * This library is distributed in the hope that it will be useful,
36
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
37
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38
+ * Lesser General Public License for more details.
39
+ *
40
+ * You should have received a copy of the GNU Lesser General Public
41
+ * License along with this library; if not, write to the Free Software
42
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
43
+ * MA 02111-1307 USA
44
+ *
45
+ * @category Crypt
46
+ * @package Crypt_TripleDES
47
+ * @author Jim Wigginton <terrafrost@php.net>
48
+ * @copyright MMVII Jim Wigginton
49
+ * @license http://www.gnu.org/licenses/lgpl.txt
50
+ * @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $
51
+ * @link http://phpseclib.sourceforge.net
52
+ */
53
+
54
+ /**
55
+ * Include Crypt_DES
56
+ */
57
+ require_once 'DES.php';
58
+
59
+ /**
60
+ * Encrypt / decrypt using inner chaining
61
+ *
62
+ * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3).
63
+ */
64
+ define('CRYPT_DES_MODE_3CBC', 3);
65
+
66
+ /**
67
+ * Encrypt / decrypt using outer chaining
68
+ *
69
+ * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC.
70
+ */
71
+ define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC);
72
+
73
+ /**
74
+ * Pure-PHP implementation of Triple DES.
75
+ *
76
+ * @author Jim Wigginton <terrafrost@php.net>
77
+ * @version 0.1.0
78
+ * @access public
79
+ * @package Crypt_TerraDES
80
+ */
81
+ class Crypt_TripleDES {
82
+ /**
83
+ * The Three Keys
84
+ *
85
+ * @see Crypt_TripleDES::setKey()
86
+ * @var String
87
+ * @access private
88
+ */
89
+ var $key = "\0\0\0\0\0\0\0\0";
90
+
91
+ /**
92
+ * The Encryption Mode
93
+ *
94
+ * @see Crypt_TripleDES::Crypt_TripleDES()
95
+ * @var Integer
96
+ * @access private
97
+ */
98
+ var $mode = CRYPT_DES_MODE_CBC;
99
+
100
+ /**
101
+ * Continuous Buffer status
102
+ *
103
+ * @see Crypt_TripleDES::enableContinuousBuffer()
104
+ * @var Boolean
105
+ * @access private
106
+ */
107
+ var $continuousBuffer = false;
108
+
109
+ /**
110
+ * Padding status
111
+ *
112
+ * @see Crypt_TripleDES::enablePadding()
113
+ * @var Boolean
114
+ * @access private
115
+ */
116
+ var $padding = true;
117
+
118
+ /**
119
+ * The Initialization Vector
120
+ *
121
+ * @see Crypt_TripleDES::setIV()
122
+ * @var String
123
+ * @access private
124
+ */
125
+ var $iv = "\0\0\0\0\0\0\0\0";
126
+
127
+ /**
128
+ * A "sliding" Initialization Vector
129
+ *
130
+ * @see Crypt_TripleDES::enableContinuousBuffer()
131
+ * @var String
132
+ * @access private
133
+ */
134
+ var $encryptIV = "\0\0\0\0\0\0\0\0";
135
+
136
+ /**
137
+ * A "sliding" Initialization Vector
138
+ *
139
+ * @see Crypt_TripleDES::enableContinuousBuffer()
140
+ * @var String
141
+ * @access private
142
+ */
143
+ var $decryptIV = "\0\0\0\0\0\0\0\0";
144
+
145
+ /**
146
+ * The Crypt_DES objects
147
+ *
148
+ * @var Array
149
+ * @access private
150
+ */
151
+ var $des;
152
+
153
+ /**
154
+ * mcrypt resource for encryption
155
+ *
156
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
157
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
158
+ *
159
+ * @see Crypt_AES::encrypt()
160
+ * @var String
161
+ * @access private
162
+ */
163
+ var $enmcrypt;
164
+
165
+ /**
166
+ * mcrypt resource for decryption
167
+ *
168
+ * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
169
+ * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
170
+ *
171
+ * @see Crypt_AES::decrypt()
172
+ * @var String
173
+ * @access private
174
+ */
175
+ var $demcrypt;
176
+
177
+ /**
178
+ * Does the (en|de)mcrypt resource need to be (re)initialized?
179
+ *
180
+ * @see setKey()
181
+ * @see setIV()
182
+ * @var Boolean
183
+ * @access private
184
+ */
185
+ var $changed = true;
186
+
187
+ /**
188
+ * Default Constructor.
189
+ *
190
+ * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
191
+ * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
192
+ *
193
+ * @param optional Integer $mode
194
+ * @return Crypt_TripleDES
195
+ * @access public
196
+ */
197
+ function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC)
198
+ {
199
+ if ( !defined('CRYPT_DES_MODE') ) {
200
+ switch (true) {
201
+ case extension_loaded('mcrypt'):
202
+ // i'd check to see if des was supported, by doing in_array('des', mcrypt_list_algorithms('')),
203
+ // but since that can be changed after the object has been created, there doesn't seem to be
204
+ // a lot of point...
205
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
206
+ break;
207
+ default:
208
+ define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
209
+ }
210
+ }
211
+
212
+ if ( $mode == CRYPT_DES_MODE_3CBC ) {
213
+ $this->mode = CRYPT_DES_MODE_3CBC;
214
+ $this->des = array(
215
+ new Crypt_DES(CRYPT_DES_MODE_CBC),
216
+ new Crypt_DES(CRYPT_DES_MODE_CBC),
217
+ new Crypt_DES(CRYPT_DES_MODE_CBC)
218
+ );
219
+
220
+ // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
221
+ $this->des[0]->disablePadding();
222
+ $this->des[1]->disablePadding();
223
+ $this->des[2]->disablePadding();
224
+
225
+ return;
226
+ }
227
+
228
+ switch ( CRYPT_DES_MODE ) {
229
+ case CRYPT_DES_MODE_MCRYPT:
230
+ switch ($mode) {
231
+ case CRYPT_DES_MODE_ECB:
232
+ $this->mode = MCRYPT_MODE_ECB;
233
+ break;
234
+ case CRYPT_DES_MODE_CTR:
235
+ $this->mode = 'ctr';
236
+ break;
237
+ case CRYPT_DES_MODE_CBC:
238
+ default:
239
+ $this->mode = MCRYPT_MODE_CBC;
240
+ }
241
+
242
+ break;
243
+ default:
244
+ $this->des = array(
245
+ new Crypt_DES(CRYPT_DES_MODE_ECB),
246
+ new Crypt_DES(CRYPT_DES_MODE_ECB),
247
+ new Crypt_DES(CRYPT_DES_MODE_ECB)
248
+ );
249
+
250
+ // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
251
+ $this->des[0]->disablePadding();
252
+ $this->des[1]->disablePadding();
253
+ $this->des[2]->disablePadding();
254
+
255
+ switch ($mode) {
256
+ case CRYPT_DES_MODE_ECB:
257
+ case CRYPT_DES_MODE_CTR:
258
+ case CRYPT_DES_MODE_CBC:
259
+ $this->mode = $mode;
260
+ break;
261
+ default:
262
+ $this->mode = CRYPT_DES_MODE_CBC;
263
+ }
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Sets the key.
269
+ *
270
+ * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or
271
+ * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate.
272
+ *
273
+ * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
274
+ *
275
+ * If the key is not explicitly set, it'll be assumed to be all zero's.
276
+ *
277
+ * @access public
278
+ * @param String $key
279
+ */
280
+ function setKey($key)
281
+ {
282
+ $length = strlen($key);
283
+ if ($length > 8) {
284
+ $key = str_pad($key, 24, chr(0));
285
+ // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
286
+ // http://php.net/function.mcrypt-encrypt#47973
287
+ //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
288
+ }
289
+ $this->key = $key;
290
+ switch (true) {
291
+ case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL:
292
+ case $this->mode == CRYPT_DES_MODE_3CBC:
293
+ $this->des[0]->setKey(substr($key, 0, 8));
294
+ $this->des[1]->setKey(substr($key, 8, 8));
295
+ $this->des[2]->setKey(substr($key, 16, 8));
296
+ }
297
+ $this->changed = true;
298
+ }
299
+
300
+ /**
301
+ * Sets the initialization vector. (optional)
302
+ *
303
+ * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
304
+ * to be all zero's.
305
+ *
306
+ * @access public
307
+ * @param String $iv
308
+ */
309
+ function setIV($iv)
310
+ {
311
+ $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
312
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
313
+ $this->des[0]->setIV($iv);
314
+ $this->des[1]->setIV($iv);
315
+ $this->des[2]->setIV($iv);
316
+ }
317
+ $this->changed = true;
318
+ }
319
+
320
+ /**
321
+ * Generate CTR XOR encryption key
322
+ *
323
+ * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
324
+ * plaintext / ciphertext in CTR mode.
325
+ *
326
+ * @see Crypt_DES::decrypt()
327
+ * @see Crypt_DES::encrypt()
328
+ * @access public
329
+ * @param Integer $length
330
+ * @param String $iv
331
+ */
332
+ function _generate_xor($length, &$iv)
333
+ {
334
+ $xor = '';
335
+ $num_blocks = ($length + 7) >> 3;
336
+ for ($i = 0; $i < $num_blocks; $i++) {
337
+ $xor.= $iv;
338
+ for ($j = 4; $j <= 8; $j+=4) {
339
+ $temp = substr($iv, -$j, 4);
340
+ switch ($temp) {
341
+ case "\xFF\xFF\xFF\xFF":
342
+ $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
343
+ break;
344
+ case "\x7F\xFF\xFF\xFF":
345
+ $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
346
+ break 2;
347
+ default:
348
+ extract(unpack('Ncount', $temp));
349
+ $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
350
+ break 2;
351
+ }
352
+ }
353
+ }
354
+
355
+ return $xor;
356
+ }
357
+
358
+ /**
359
+ * Encrypts a message.
360
+ *
361
+ * @access public
362
+ * @param String $plaintext
363
+ */
364
+ function encrypt($plaintext)
365
+ {
366
+ if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
367
+ $plaintext = $this->_pad($plaintext);
368
+ }
369
+
370
+ // if the key is smaller then 8, do what we'd normally do
371
+ if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
372
+ $ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext)));
373
+
374
+ return $ciphertext;
375
+ }
376
+
377
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
378
+ if ($this->changed) {
379
+ if (!isset($this->enmcrypt)) {
380
+ $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
381
+ }
382
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
383
+ $this->changed = false;
384
+ }
385
+
386
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
387
+
388
+ if (!$this->continuousBuffer) {
389
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
390
+ }
391
+
392
+ return $ciphertext;
393
+ }
394
+
395
+ if (strlen($this->key) <= 8) {
396
+ $this->des[0]->mode = $this->mode;
397
+
398
+ return $this->des[0]->encrypt($plaintext);
399
+ }
400
+
401
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
402
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
403
+ $plaintext = str_pad($plaintext, ceil(strlen($plaintext) / 8) * 8, chr(0));
404
+
405
+ $des = $this->des;
406
+
407
+ $ciphertext = '';
408
+ switch ($this->mode) {
409
+ case CRYPT_DES_MODE_ECB:
410
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
411
+ $block = substr($plaintext, $i, 8);
412
+ $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
413
+ $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
414
+ $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
415
+ $ciphertext.= $block;
416
+ }
417
+ break;
418
+ case CRYPT_DES_MODE_CBC:
419
+ $xor = $this->encryptIV;
420
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
421
+ $block = substr($plaintext, $i, 8) ^ $xor;
422
+ $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
423
+ $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
424
+ $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
425
+ $xor = $block;
426
+ $ciphertext.= $block;
427
+ }
428
+ if ($this->continuousBuffer) {
429
+ $this->encryptIV = $xor;
430
+ }
431
+ break;
432
+ case CRYPT_DES_MODE_CTR:
433
+ $xor = $this->encryptIV;
434
+ for ($i = 0; $i < strlen($plaintext); $i+=8) {
435
+ $key = $this->_generate_xor(8, $xor);
436
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
437
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
438
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
439
+ $block = substr($plaintext, $i, 8);
440
+ $ciphertext.= $block ^ $key;
441
+ }
442
+ if ($this->continuousBuffer) {
443
+ $this->encryptIV = $xor;
444
+ }
445
+ }
446
+
447
+ return $ciphertext;
448
+ }
449
+
450
+ /**
451
+ * Decrypts a message.
452
+ *
453
+ * @access public
454
+ * @param String $ciphertext
455
+ */
456
+ function decrypt($ciphertext)
457
+ {
458
+ if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
459
+ $plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext)));
460
+
461
+ return $this->_unpad($plaintext);
462
+ }
463
+
464
+ // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
465
+ // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
466
+ $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
467
+
468
+ if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
469
+ if ($this->changed) {
470
+ if (!isset($this->demcrypt)) {
471
+ $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
472
+ }
473
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
474
+ $this->changed = false;
475
+ }
476
+
477
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
478
+
479
+ if (!$this->continuousBuffer) {
480
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
481
+ }
482
+
483
+ return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
484
+ }
485
+
486
+ if (strlen($this->key) <= 8) {
487
+ $this->des[0]->mode = $this->mode;
488
+
489
+ return $this->_unpad($this->des[0]->decrypt($plaintext));
490
+ }
491
+
492
+ $des = $this->des;
493
+
494
+ $plaintext = '';
495
+ switch ($this->mode) {
496
+ case CRYPT_DES_MODE_ECB:
497
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
498
+ $block = substr($ciphertext, $i, 8);
499
+ $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
500
+ $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
501
+ $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
502
+ $plaintext.= $block;
503
+ }
504
+ break;
505
+ case CRYPT_DES_MODE_CBC:
506
+ $xor = $this->decryptIV;
507
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
508
+ $orig = $block = substr($ciphertext, $i, 8);
509
+ $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
510
+ $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
511
+ $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
512
+ $plaintext.= $block ^ $xor;
513
+ $xor = $orig;
514
+ }
515
+ if ($this->continuousBuffer) {
516
+ $this->decryptIV = $xor;
517
+ }
518
+ break;
519
+ case CRYPT_DES_MODE_CTR:
520
+ $xor = $this->decryptIV;
521
+ for ($i = 0; $i < strlen($ciphertext); $i+=8) {
522
+ $key = $this->_generate_xor(8, $xor);
523
+ $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
524
+ $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
525
+ $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
526
+ $block = substr($ciphertext, $i, 8);
527
+ $plaintext.= $block ^ $key;
528
+ }
529
+ if ($this->continuousBuffer) {
530
+ $this->decryptIV = $xor;
531
+ }
532
+ }
533
+
534
+ return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
535
+ }
536
+
537
+ /**
538
+ * Treat consecutive "packets" as if they are a continuous buffer.
539
+ *
540
+ * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
541
+ * will yield different outputs:
542
+ *
543
+ * <code>
544
+ * echo $des->encrypt(substr($plaintext, 0, 8));
545
+ * echo $des->encrypt(substr($plaintext, 8, 8));
546
+ * </code>
547
+ * <code>
548
+ * echo $des->encrypt($plaintext);
549
+ * </code>
550
+ *
551
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
552
+ * another, as demonstrated with the following:
553
+ *
554
+ * <code>
555
+ * $des->encrypt(substr($plaintext, 0, 8));
556
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
557
+ * </code>
558
+ * <code>
559
+ * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
560
+ * </code>
561
+ *
562
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
563
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
564
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
565
+ *
566
+ * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
567
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
568
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
569
+ * however, they are also less intuitive and more likely to cause you problems.
570
+ *
571
+ * @see Crypt_TripleDES::disableContinuousBuffer()
572
+ * @access public
573
+ */
574
+ function enableContinuousBuffer()
575
+ {
576
+ $this->continuousBuffer = true;
577
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
578
+ $this->des[0]->enableContinuousBuffer();
579
+ $this->des[1]->enableContinuousBuffer();
580
+ $this->des[2]->enableContinuousBuffer();
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Treat consecutive packets as if they are a discontinuous buffer.
586
+ *
587
+ * The default behavior.
588
+ *
589
+ * @see Crypt_TripleDES::enableContinuousBuffer()
590
+ * @access public
591
+ */
592
+ function disableContinuousBuffer()
593
+ {
594
+ $this->continuousBuffer = false;
595
+ $this->encryptIV = $this->iv;
596
+ $this->decryptIV = $this->iv;
597
+
598
+ if ($this->mode == CRYPT_DES_MODE_3CBC) {
599
+ $this->des[0]->disableContinuousBuffer();
600
+ $this->des[1]->disableContinuousBuffer();
601
+ $this->des[2]->disableContinuousBuffer();
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Pad "packets".
607
+ *
608
+ * DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
609
+ * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
610
+ *
611
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
612
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
613
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
614
+ * transmitted separately)
615
+ *
616
+ * @see Crypt_TripleDES::disablePadding()
617
+ * @access public
618
+ */
619
+ function enablePadding()
620
+ {
621
+ $this->padding = true;
622
+ }
623
+
624
+ /**
625
+ * Do not pad packets.
626
+ *
627
+ * @see Crypt_TripleDES::enablePadding()
628
+ * @access public
629
+ */
630
+ function disablePadding()
631
+ {
632
+ $this->padding = false;
633
+ }
634
+
635
+ /**
636
+ * Pads a string
637
+ *
638
+ * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
639
+ * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
640
+ *
641
+ * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
642
+ * and padding will, hence forth, be enabled.
643
+ *
644
+ * @see Crypt_TripleDES::_unpad()
645
+ * @access private
646
+ */
647
+ function _pad($text)
648
+ {
649
+ $length = strlen($text);
650
+
651
+ if (!$this->padding) {
652
+ if (($length & 7) == 0) {
653
+ return $text;
654
+ } else {
655
+ user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
656
+ $this->padding = true;
657
+ }
658
+ }
659
+
660
+ $pad = 8 - ($length & 7);
661
+ return str_pad($text, $length + $pad, chr($pad));
662
+ }
663
+
664
+ /**
665
+ * Unpads a string
666
+ *
667
+ * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
668
+ * and false will be returned.
669
+ *
670
+ * @see Crypt_TripleDES::_pad()
671
+ * @access private
672
+ */
673
+ function _unpad($text)
674
+ {
675
+ if (!$this->padding) {
676
+ return $text;
677
+ }
678
+
679
+ $length = ord($text[strlen($text) - 1]);
680
+
681
+ if (!$length || $length > 8) {
682
+ return false;
683
+ }
684
+
685
+ return substr($text, 0, -$length);
686
+ }
687
+ }
688
+
689
+ // vim: ts=4:sw=4:et:
690
+ // vim6: fdl=1:
app/code/local/Wisepricer/Syncer/lib/phpseclib/Math/BigInteger.php ADDED
@@ -0,0 +1,3545 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP arbitrary precision integer arithmetic library.
6
+ *
7
+ * Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available,
8
+ * and an internal implementation, otherwise.
9
+ *
10
+ * PHP versions 4 and 5
11
+ *
12
+ * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
13
+ * {@link MATH_BIGINTEGER_MODE_INTERNAL MATH_BIGINTEGER_MODE_INTERNAL} mode)
14
+ *
15
+ * Math_BigInteger uses base-2**26 to perform operations such as multiplication and division and
16
+ * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible
17
+ * value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
18
+ * point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
19
+ * used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
20
+ * which only supports integers. Although this fact will slow this library down, the fact that such a high
21
+ * base is being used should more than compensate.
22
+ *
23
+ * When PHP version 6 is officially released, we'll be able to use 64-bit integers. This should, once again,
24
+ * allow bitwise operators, and will increase the maximum possible base to 2**31 (or 2**62 for addition /
25
+ * subtraction).
26
+ *
27
+ * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
28
+ * (new Math_BigInteger(pow(2, 26)))->value = array(0, 1)
29
+ *
30
+ * Useful resources are as follows:
31
+ *
32
+ * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
33
+ * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
34
+ * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
35
+ *
36
+ * Here's an example of how to use this library:
37
+ * <code>
38
+ * <?php
39
+ * include('Math/BigInteger.php');
40
+ *
41
+ * $a = new Math_BigInteger(2);
42
+ * $b = new Math_BigInteger(3);
43
+ *
44
+ * $c = $a->add($b);
45
+ *
46
+ * echo $c->toString(); // outputs 5
47
+ * ?>
48
+ * </code>
49
+ *
50
+ * LICENSE: This library is free software; you can redistribute it and/or
51
+ * modify it under the terms of the GNU Lesser General Public
52
+ * License as published by the Free Software Foundation; either
53
+ * version 2.1 of the License, or (at your option) any later version.
54
+ *
55
+ * This library is distributed in the hope that it will be useful,
56
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
57
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
58
+ * Lesser General Public License for more details.
59
+ *
60
+ * You should have received a copy of the GNU Lesser General Public
61
+ * License along with this library; if not, write to the Free Software
62
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
63
+ * MA 02111-1307 USA
64
+ *
65
+ * @category Math
66
+ * @package Math_BigInteger
67
+ * @author Jim Wigginton <terrafrost@php.net>
68
+ * @copyright MMVI Jim Wigginton
69
+ * @license http://www.gnu.org/licenses/lgpl.txt
70
+ * @version $Id: BigInteger.php,v 1.33 2010/03/22 22:32:03 terrafrost Exp $
71
+ * @link http://pear.php.net/package/Math_BigInteger
72
+ */
73
+
74
+ /**#@+
75
+ * Reduction constants
76
+ *
77
+ * @access private
78
+ * @see Math_BigInteger::_reduce()
79
+ */
80
+ /**
81
+ * @see Math_BigInteger::_montgomery()
82
+ * @see Math_BigInteger::_prepMontgomery()
83
+ */
84
+ define('MATH_BIGINTEGER_MONTGOMERY', 0);
85
+ /**
86
+ * @see Math_BigInteger::_barrett()
87
+ */
88
+ define('MATH_BIGINTEGER_BARRETT', 1);
89
+ /**
90
+ * @see Math_BigInteger::_mod2()
91
+ */
92
+ define('MATH_BIGINTEGER_POWEROF2', 2);
93
+ /**
94
+ * @see Math_BigInteger::_remainder()
95
+ */
96
+ define('MATH_BIGINTEGER_CLASSIC', 3);
97
+ /**
98
+ * @see Math_BigInteger::__clone()
99
+ */
100
+ define('MATH_BIGINTEGER_NONE', 4);
101
+ /**#@-*/
102
+
103
+ /**#@+
104
+ * Array constants
105
+ *
106
+ * Rather than create a thousands and thousands of new Math_BigInteger objects in repeated function calls to add() and
107
+ * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
108
+ *
109
+ * @access private
110
+ */
111
+ /**
112
+ * $result[MATH_BIGINTEGER_VALUE] contains the value.
113
+ */
114
+ define('MATH_BIGINTEGER_VALUE', 0);
115
+ /**
116
+ * $result[MATH_BIGINTEGER_SIGN] contains the sign.
117
+ */
118
+ define('MATH_BIGINTEGER_SIGN', 1);
119
+ /**#@-*/
120
+
121
+ /**#@+
122
+ * @access private
123
+ * @see Math_BigInteger::_montgomery()
124
+ * @see Math_BigInteger::_barrett()
125
+ */
126
+ /**
127
+ * Cache constants
128
+ *
129
+ * $cache[MATH_BIGINTEGER_VARIABLE] tells us whether or not the cached data is still valid.
130
+ */
131
+ define('MATH_BIGINTEGER_VARIABLE', 0);
132
+ /**
133
+ * $cache[MATH_BIGINTEGER_DATA] contains the cached data.
134
+ */
135
+ define('MATH_BIGINTEGER_DATA', 1);
136
+ /**#@-*/
137
+
138
+ /**#@+
139
+ * Mode constants.
140
+ *
141
+ * @access private
142
+ * @see Math_BigInteger::Math_BigInteger()
143
+ */
144
+ /**
145
+ * To use the pure-PHP implementation
146
+ */
147
+ define('MATH_BIGINTEGER_MODE_INTERNAL', 1);
148
+ /**
149
+ * To use the BCMath library
150
+ *
151
+ * (if enabled; otherwise, the internal implementation will be used)
152
+ */
153
+ define('MATH_BIGINTEGER_MODE_BCMATH', 2);
154
+ /**
155
+ * To use the GMP library
156
+ *
157
+ * (if present; otherwise, either the BCMath or the internal implementation will be used)
158
+ */
159
+ define('MATH_BIGINTEGER_MODE_GMP', 3);
160
+ /**#@-*/
161
+
162
+ /**
163
+ * The largest digit that may be used in addition / subtraction
164
+ *
165
+ * (we do pow(2, 52) instead of using 4503599627370496, directly, because some PHP installations
166
+ * will truncate 4503599627370496)
167
+ *
168
+ * @access private
169
+ */
170
+ define('MATH_BIGINTEGER_MAX_DIGIT52', pow(2, 52));
171
+
172
+ /**
173
+ * Karatsuba Cutoff
174
+ *
175
+ * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
176
+ *
177
+ * @access private
178
+ */
179
+ define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25);
180
+
181
+ /**
182
+ * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
183
+ * numbers.
184
+ *
185
+ * @author Jim Wigginton <terrafrost@php.net>
186
+ * @version 1.0.0RC4
187
+ * @access public
188
+ * @package Math_BigInteger
189
+ */
190
+ class Math_BigInteger {
191
+ /**
192
+ * Holds the BigInteger's value.
193
+ *
194
+ * @var Array
195
+ * @access private
196
+ */
197
+ var $value;
198
+
199
+ /**
200
+ * Holds the BigInteger's magnitude.
201
+ *
202
+ * @var Boolean
203
+ * @access private
204
+ */
205
+ var $is_negative = false;
206
+
207
+ /**
208
+ * Random number generator function
209
+ *
210
+ * @see setRandomGenerator()
211
+ * @access private
212
+ */
213
+ var $generator = 'mt_rand';
214
+
215
+ /**
216
+ * Precision
217
+ *
218
+ * @see setPrecision()
219
+ * @access private
220
+ */
221
+ var $precision = -1;
222
+
223
+ /**
224
+ * Precision Bitmask
225
+ *
226
+ * @see setPrecision()
227
+ * @access private
228
+ */
229
+ var $bitmask = false;
230
+
231
+ /**
232
+ * Mode independant value used for serialization.
233
+ *
234
+ * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
235
+ * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value,
236
+ * however, $this->hex is only calculated when $this->__sleep() is called.
237
+ *
238
+ * @see __sleep()
239
+ * @see __wakeup()
240
+ * @var String
241
+ * @access private
242
+ */
243
+ var $hex;
244
+
245
+ /**
246
+ * Converts base-2, base-10, base-16, and binary strings (eg. base-256) to BigIntegers.
247
+ *
248
+ * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
249
+ * two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
250
+ *
251
+ * Here's an example:
252
+ * <code>
253
+ * <?php
254
+ * include('Math/BigInteger.php');
255
+ *
256
+ * $a = new Math_BigInteger('0x32', 16); // 50 in base-16
257
+ *
258
+ * echo $a->toString(); // outputs 50
259
+ * ?>
260
+ * </code>
261
+ *
262
+ * @param optional $x base-10 number or base-$base number if $base set.
263
+ * @param optional integer $base
264
+ * @return Math_BigInteger
265
+ * @access public
266
+ */
267
+ function Math_BigInteger($x = 0, $base = 10)
268
+ {
269
+ if ( !defined('MATH_BIGINTEGER_MODE') ) {
270
+ switch (true) {
271
+ case extension_loaded('gmp'):
272
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP);
273
+ break;
274
+ case extension_loaded('bcmath'):
275
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH);
276
+ break;
277
+ default:
278
+ define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL);
279
+ }
280
+ }
281
+
282
+ switch ( MATH_BIGINTEGER_MODE ) {
283
+ case MATH_BIGINTEGER_MODE_GMP:
284
+ if (is_resource($x) && get_resource_type($x) == 'GMP integer') {
285
+ $this->value = $x;
286
+ return;
287
+ }
288
+ $this->value = gmp_init(0);
289
+ break;
290
+ case MATH_BIGINTEGER_MODE_BCMATH:
291
+ $this->value = '0';
292
+ break;
293
+ default:
294
+ $this->value = array();
295
+ }
296
+
297
+ if (empty($x)) {
298
+ return;
299
+ }
300
+
301
+ switch ($base) {
302
+ case -256:
303
+ if (ord($x[0]) & 0x80) {
304
+ $x = ~$x;
305
+ $this->is_negative = true;
306
+ }
307
+ case 256:
308
+ switch ( MATH_BIGINTEGER_MODE ) {
309
+ case MATH_BIGINTEGER_MODE_GMP:
310
+ $sign = $this->is_negative ? '-' : '';
311
+ $this->value = gmp_init($sign . '0x' . bin2hex($x));
312
+ break;
313
+ case MATH_BIGINTEGER_MODE_BCMATH:
314
+ // round $len to the nearest 4 (thanks, DavidMJ!)
315
+ $len = (strlen($x) + 3) & 0xFFFFFFFC;
316
+
317
+ $x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
318
+
319
+ for ($i = 0; $i < $len; $i+= 4) {
320
+ $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
321
+ $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
322
+ }
323
+
324
+ if ($this->is_negative) {
325
+ $this->value = '-' . $this->value;
326
+ }
327
+
328
+ break;
329
+ // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
330
+ default:
331
+ while (strlen($x)) {
332
+ $this->value[] = $this->_bytes2int($this->_base256_rshift($x, 26));
333
+ }
334
+ }
335
+
336
+ if ($this->is_negative) {
337
+ if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) {
338
+ $this->is_negative = false;
339
+ }
340
+ $temp = $this->add(new Math_BigInteger('-1'));
341
+ $this->value = $temp->value;
342
+ }
343
+ break;
344
+ case 16:
345
+ case -16:
346
+ if ($base > 0 && $x[0] == '-') {
347
+ $this->is_negative = true;
348
+ $x = substr($x, 1);
349
+ }
350
+
351
+ $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
352
+
353
+ $is_negative = false;
354
+ if ($base < 0 && hexdec($x[0]) >= 8) {
355
+ $this->is_negative = $is_negative = true;
356
+ $x = bin2hex(~pack('H*', $x));
357
+ }
358
+
359
+ switch ( MATH_BIGINTEGER_MODE ) {
360
+ case MATH_BIGINTEGER_MODE_GMP:
361
+ $temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
362
+ $this->value = gmp_init($temp);
363
+ $this->is_negative = false;
364
+ break;
365
+ case MATH_BIGINTEGER_MODE_BCMATH:
366
+ $x = ( strlen($x) & 1 ) ? '0' . $x : $x;
367
+ $temp = new Math_BigInteger(pack('H*', $x), 256);
368
+ $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
369
+ $this->is_negative = false;
370
+ break;
371
+ default:
372
+ $x = ( strlen($x) & 1 ) ? '0' . $x : $x;
373
+ $temp = new Math_BigInteger(pack('H*', $x), 256);
374
+ $this->value = $temp->value;
375
+ }
376
+
377
+ if ($is_negative) {
378
+ $temp = $this->add(new Math_BigInteger('-1'));
379
+ $this->value = $temp->value;
380
+ }
381
+ break;
382
+ case 10:
383
+ case -10:
384
+ $x = preg_replace('#^(-?[0-9]*).*#', '$1', $x);
385
+
386
+ switch ( MATH_BIGINTEGER_MODE ) {
387
+ case MATH_BIGINTEGER_MODE_GMP:
388
+ $this->value = gmp_init($x);
389
+ break;
390
+ case MATH_BIGINTEGER_MODE_BCMATH:
391
+ // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
392
+ // results then doing it on '-1' does (modInverse does $x[0])
393
+ $this->value = (string) $x;
394
+ break;
395
+ default:
396
+ $temp = new Math_BigInteger();
397
+
398
+ // array(10000000) is 10**7 in base-2**26. 10**7 is the closest to 2**26 we can get without passing it.
399
+ $multiplier = new Math_BigInteger();
400
+ $multiplier->value = array(10000000);
401
+
402
+ if ($x[0] == '-') {
403
+ $this->is_negative = true;
404
+ $x = substr($x, 1);
405
+ }
406
+
407
+ $x = str_pad($x, strlen($x) + (6 * strlen($x)) % 7, 0, STR_PAD_LEFT);
408
+
409
+ while (strlen($x)) {
410
+ $temp = $temp->multiply($multiplier);
411
+ $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, 7)), 256));
412
+ $x = substr($x, 7);
413
+ }
414
+
415
+ $this->value = $temp->value;
416
+ }
417
+ break;
418
+ case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
419
+ case -2:
420
+ if ($base > 0 && $x[0] == '-') {
421
+ $this->is_negative = true;
422
+ $x = substr($x, 1);
423
+ }
424
+
425
+ $x = preg_replace('#^([01]*).*#', '$1', $x);
426
+ $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
427
+
428
+ $str = '0x';
429
+ while (strlen($x)) {
430
+ $part = substr($x, 0, 4);
431
+ $str.= dechex(bindec($part));
432
+ $x = substr($x, 4);
433
+ }
434
+
435
+ if ($this->is_negative) {
436
+ $str = '-' . $str;
437
+ }
438
+
439
+ $temp = new Math_BigInteger($str, 8 * $base); // ie. either -16 or +16
440
+ $this->value = $temp->value;
441
+ $this->is_negative = $temp->is_negative;
442
+
443
+ break;
444
+ default:
445
+ // base not supported, so we'll let $this == 0
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Converts a BigInteger to a byte string (eg. base-256).
451
+ *
452
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
453
+ * saved as two's compliment.
454
+ *
455
+ * Here's an example:
456
+ * <code>
457
+ * <?php
458
+ * include('Math/BigInteger.php');
459
+ *
460
+ * $a = new Math_BigInteger('65');
461
+ *
462
+ * echo $a->toBytes(); // outputs chr(65)
463
+ * ?>
464
+ * </code>
465
+ *
466
+ * @param Boolean $twos_compliment
467
+ * @return String
468
+ * @access public
469
+ * @internal Converts a base-2**26 number to base-2**8
470
+ */
471
+ function toBytes($twos_compliment = false)
472
+ {
473
+ if ($twos_compliment) {
474
+ $comparison = $this->compare(new Math_BigInteger());
475
+ if ($comparison == 0) {
476
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
477
+ }
478
+
479
+ $temp = $comparison < 0 ? $this->add(new Math_BigInteger(1)) : $this->copy();
480
+ $bytes = $temp->toBytes();
481
+
482
+ if (empty($bytes)) { // eg. if the number we're trying to convert is -1
483
+ $bytes = chr(0);
484
+ }
485
+
486
+ if (ord($bytes[0]) & 0x80) {
487
+ $bytes = chr(0) . $bytes;
488
+ }
489
+
490
+ return $comparison < 0 ? ~$bytes : $bytes;
491
+ }
492
+
493
+ switch ( MATH_BIGINTEGER_MODE ) {
494
+ case MATH_BIGINTEGER_MODE_GMP:
495
+ if (gmp_cmp($this->value, gmp_init(0)) == 0) {
496
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
497
+ }
498
+
499
+ $temp = gmp_strval(gmp_abs($this->value), 16);
500
+ $temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp;
501
+ $temp = pack('H*', $temp);
502
+
503
+ return $this->precision > 0 ?
504
+ substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
505
+ ltrim($temp, chr(0));
506
+ case MATH_BIGINTEGER_MODE_BCMATH:
507
+ if ($this->value === '0') {
508
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
509
+ }
510
+
511
+ $value = '';
512
+ $current = $this->value;
513
+
514
+ if ($current[0] == '-') {
515
+ $current = substr($current, 1);
516
+ }
517
+
518
+ while (bccomp($current, '0', 0) > 0) {
519
+ $temp = bcmod($current, '16777216');
520
+ $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
521
+ $current = bcdiv($current, '16777216', 0);
522
+ }
523
+
524
+ return $this->precision > 0 ?
525
+ substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
526
+ ltrim($value, chr(0));
527
+ }
528
+
529
+ if (!count($this->value)) {
530
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
531
+ }
532
+ $result = $this->_int2bytes($this->value[count($this->value) - 1]);
533
+
534
+ $temp = $this->copy();
535
+
536
+ for ($i = count($temp->value) - 2; $i >= 0; --$i) {
537
+ $temp->_base256_lshift($result, 26);
538
+ $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
539
+ }
540
+
541
+ return $this->precision > 0 ?
542
+ str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
543
+ $result;
544
+ }
545
+
546
+ /**
547
+ * Converts a BigInteger to a hex string (eg. base-16)).
548
+ *
549
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
550
+ * saved as two's compliment.
551
+ *
552
+ * Here's an example:
553
+ * <code>
554
+ * <?php
555
+ * include('Math/BigInteger.php');
556
+ *
557
+ * $a = new Math_BigInteger('65');
558
+ *
559
+ * echo $a->toHex(); // outputs '41'
560
+ * ?>
561
+ * </code>
562
+ *
563
+ * @param Boolean $twos_compliment
564
+ * @return String
565
+ * @access public
566
+ * @internal Converts a base-2**26 number to base-2**8
567
+ */
568
+ function toHex($twos_compliment = false)
569
+ {
570
+ return bin2hex($this->toBytes($twos_compliment));
571
+ }
572
+
573
+ /**
574
+ * Converts a BigInteger to a bit string (eg. base-2).
575
+ *
576
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
577
+ * saved as two's compliment.
578
+ *
579
+ * Here's an example:
580
+ * <code>
581
+ * <?php
582
+ * include('Math/BigInteger.php');
583
+ *
584
+ * $a = new Math_BigInteger('65');
585
+ *
586
+ * echo $a->toBits(); // outputs '1000001'
587
+ * ?>
588
+ * </code>
589
+ *
590
+ * @param Boolean $twos_compliment
591
+ * @return String
592
+ * @access public
593
+ * @internal Converts a base-2**26 number to base-2**2
594
+ */
595
+ function toBits($twos_compliment = false)
596
+ {
597
+ $hex = $this->toHex($twos_compliment);
598
+ $bits = '';
599
+ for ($i = 0; $i < strlen($hex); $i+=8) {
600
+ $bits.= str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT);
601
+ }
602
+ return $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
603
+ }
604
+
605
+ /**
606
+ * Converts a BigInteger to a base-10 number.
607
+ *
608
+ * Here's an example:
609
+ * <code>
610
+ * <?php
611
+ * include('Math/BigInteger.php');
612
+ *
613
+ * $a = new Math_BigInteger('50');
614
+ *
615
+ * echo $a->toString(); // outputs 50
616
+ * ?>
617
+ * </code>
618
+ *
619
+ * @return String
620
+ * @access public
621
+ * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
622
+ */
623
+ function toString()
624
+ {
625
+ switch ( MATH_BIGINTEGER_MODE ) {
626
+ case MATH_BIGINTEGER_MODE_GMP:
627
+ return gmp_strval($this->value);
628
+ case MATH_BIGINTEGER_MODE_BCMATH:
629
+ if ($this->value === '0') {
630
+ return '0';
631
+ }
632
+
633
+ return ltrim($this->value, '0');
634
+ }
635
+
636
+ if (!count($this->value)) {
637
+ return '0';
638
+ }
639
+
640
+ $temp = $this->copy();
641
+ $temp->is_negative = false;
642
+
643
+ $divisor = new Math_BigInteger();
644
+ $divisor->value = array(10000000); // eg. 10**7
645
+ $result = '';
646
+ while (count($temp->value)) {
647
+ list($temp, $mod) = $temp->divide($divisor);
648
+ $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', 7, '0', STR_PAD_LEFT) . $result;
649
+ }
650
+ $result = ltrim($result, '0');
651
+ if (empty($result)) {
652
+ $result = '0';
653
+ }
654
+
655
+ if ($this->is_negative) {
656
+ $result = '-' . $result;
657
+ }
658
+
659
+ return $result;
660
+ }
661
+
662
+ /**
663
+ * Copy an object
664
+ *
665
+ * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee
666
+ * that all objects are passed by value, when appropriate. More information can be found here:
667
+ *
668
+ * {@link http://php.net/language.oop5.basic#51624}
669
+ *
670
+ * @access public
671
+ * @see __clone()
672
+ * @return Math_BigInteger
673
+ */
674
+ function copy()
675
+ {
676
+ $temp = new Math_BigInteger();
677
+ $temp->value = $this->value;
678
+ $temp->is_negative = $this->is_negative;
679
+ $temp->generator = $this->generator;
680
+ $temp->precision = $this->precision;
681
+ $temp->bitmask = $this->bitmask;
682
+ return $temp;
683
+ }
684
+
685
+ /**
686
+ * __toString() magic method
687
+ *
688
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
689
+ * toString().
690
+ *
691
+ * @access public
692
+ * @internal Implemented per a suggestion by Techie-Michael - thanks!
693
+ */
694
+ function __toString()
695
+ {
696
+ return $this->toString();
697
+ }
698
+
699
+ /**
700
+ * __clone() magic method
701
+ *
702
+ * Although you can call Math_BigInteger::__toString() directly in PHP5, you cannot call Math_BigInteger::__clone()
703
+ * directly in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5
704
+ * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and PHP5,
705
+ * call Math_BigInteger::copy(), instead.
706
+ *
707
+ * @access public
708
+ * @see copy()
709
+ * @return Math_BigInteger
710
+ */
711
+ function __clone()
712
+ {
713
+ return $this->copy();
714
+ }
715
+
716
+ /**
717
+ * __sleep() magic method
718
+ *
719
+ * Will be called, automatically, when serialize() is called on a Math_BigInteger object.
720
+ *
721
+ * @see __wakeup()
722
+ * @access public
723
+ */
724
+ function __sleep()
725
+ {
726
+ $this->hex = $this->toHex(true);
727
+ $vars = array('hex');
728
+ if ($this->generator != 'mt_rand') {
729
+ $vars[] = 'generator';
730
+ }
731
+ if ($this->precision > 0) {
732
+ $vars[] = 'precision';
733
+ }
734
+ return $vars;
735
+
736
+ }
737
+
738
+ /**
739
+ * __wakeup() magic method
740
+ *
741
+ * Will be called, automatically, when unserialize() is called on a Math_BigInteger object.
742
+ *
743
+ * @see __sleep()
744
+ * @access public
745
+ */
746
+ function __wakeup()
747
+ {
748
+ $temp = new Math_BigInteger($this->hex, -16);
749
+ $this->value = $temp->value;
750
+ $this->is_negative = $temp->is_negative;
751
+ $this->setRandomGenerator($this->generator);
752
+ if ($this->precision > 0) {
753
+ // recalculate $this->bitmask
754
+ $this->setPrecision($this->precision);
755
+ }
756
+ }
757
+
758
+ /**
759
+ * Adds two BigIntegers.
760
+ *
761
+ * Here's an example:
762
+ * <code>
763
+ * <?php
764
+ * include('Math/BigInteger.php');
765
+ *
766
+ * $a = new Math_BigInteger('10');
767
+ * $b = new Math_BigInteger('20');
768
+ *
769
+ * $c = $a->add($b);
770
+ *
771
+ * echo $c->toString(); // outputs 30
772
+ * ?>
773
+ * </code>
774
+ *
775
+ * @param Math_BigInteger $y
776
+ * @return Math_BigInteger
777
+ * @access public
778
+ * @internal Performs base-2**52 addition
779
+ */
780
+ function add($y)
781
+ {
782
+ switch ( MATH_BIGINTEGER_MODE ) {
783
+ case MATH_BIGINTEGER_MODE_GMP:
784
+ $temp = new Math_BigInteger();
785
+ $temp->value = gmp_add($this->value, $y->value);
786
+
787
+ return $this->_normalize($temp);
788
+ case MATH_BIGINTEGER_MODE_BCMATH:
789
+ $temp = new Math_BigInteger();
790
+ $temp->value = bcadd($this->value, $y->value, 0);
791
+
792
+ return $this->_normalize($temp);
793
+ }
794
+
795
+ $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);
796
+
797
+ $result = new Math_BigInteger();
798
+ $result->value = $temp[MATH_BIGINTEGER_VALUE];
799
+ $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
800
+
801
+ return $this->_normalize($result);
802
+ }
803
+
804
+ /**
805
+ * Performs addition.
806
+ *
807
+ * @param Array $x_value
808
+ * @param Boolean $x_negative
809
+ * @param Array $y_value
810
+ * @param Boolean $y_negative
811
+ * @return Array
812
+ * @access private
813
+ */
814
+ function _add($x_value, $x_negative, $y_value, $y_negative)
815
+ {
816
+ $x_size = count($x_value);
817
+ $y_size = count($y_value);
818
+
819
+ if ($x_size == 0) {
820
+ return array(
821
+ MATH_BIGINTEGER_VALUE => $y_value,
822
+ MATH_BIGINTEGER_SIGN => $y_negative
823
+ );
824
+ } else if ($y_size == 0) {
825
+ return array(
826
+ MATH_BIGINTEGER_VALUE => $x_value,
827
+ MATH_BIGINTEGER_SIGN => $x_negative
828
+ );
829
+ }
830
+
831
+ // subtract, if appropriate
832
+ if ( $x_negative != $y_negative ) {
833
+ if ( $x_value == $y_value ) {
834
+ return array(
835
+ MATH_BIGINTEGER_VALUE => array(),
836
+ MATH_BIGINTEGER_SIGN => false
837
+ );
838
+ }
839
+
840
+ $temp = $this->_subtract($x_value, false, $y_value, false);
841
+ $temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
842
+ $x_negative : $y_negative;
843
+
844
+ return $temp;
845
+ }
846
+
847
+ if ($x_size < $y_size) {
848
+ $size = $x_size;
849
+ $value = $y_value;
850
+ } else {
851
+ $size = $y_size;
852
+ $value = $x_value;
853
+ }
854
+
855
+ $value[] = 0; // just in case the carry adds an extra digit
856
+
857
+ $carry = 0;
858
+ for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
859
+ $sum = $x_value[$j] * 0x4000000 + $x_value[$i] + $y_value[$j] * 0x4000000 + $y_value[$i] + $carry;
860
+ $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT52; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
861
+ $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT52 : $sum;
862
+
863
+ $temp = (int) ($sum / 0x4000000);
864
+
865
+ $value[$i] = (int) ($sum - 0x4000000 * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
866
+ $value[$j] = $temp;
867
+ }
868
+
869
+ if ($j == $size) { // ie. if $y_size is odd
870
+ $sum = $x_value[$i] + $y_value[$i] + $carry;
871
+ $carry = $sum >= 0x4000000;
872
+ $value[$i] = $carry ? $sum - 0x4000000 : $sum;
873
+ ++$i; // ie. let $i = $j since we've just done $value[$i]
874
+ }
875
+
876
+ if ($carry) {
877
+ for (; $value[$i] == 0x3FFFFFF; ++$i) {
878
+ $value[$i] = 0;
879
+ }
880
+ ++$value[$i];
881
+ }
882
+
883
+ return array(
884
+ MATH_BIGINTEGER_VALUE => $this->_trim($value),
885
+ MATH_BIGINTEGER_SIGN => $x_negative
886
+ );
887
+ }
888
+
889
+ /**
890
+ * Subtracts two BigIntegers.
891
+ *
892
+ * Here's an example:
893
+ * <code>
894
+ * <?php
895
+ * include('Math/BigInteger.php');
896
+ *
897
+ * $a = new Math_BigInteger('10');
898
+ * $b = new Math_BigInteger('20');
899
+ *
900
+ * $c = $a->subtract($b);
901
+ *
902
+ * echo $c->toString(); // outputs -10
903
+ * ?>
904
+ * </code>
905
+ *
906
+ * @param Math_BigInteger $y
907
+ * @return Math_BigInteger
908
+ * @access public
909
+ * @internal Performs base-2**52 subtraction
910
+ */
911
+ function subtract($y)
912
+ {
913
+ switch ( MATH_BIGINTEGER_MODE ) {
914
+ case MATH_BIGINTEGER_MODE_GMP:
915
+ $temp = new Math_BigInteger();
916
+ $temp->value = gmp_sub($this->value, $y->value);
917
+
918
+ return $this->_normalize($temp);
919
+ case MATH_BIGINTEGER_MODE_BCMATH:
920
+ $temp = new Math_BigInteger();
921
+ $temp->value = bcsub($this->value, $y->value, 0);
922
+
923
+ return $this->_normalize($temp);
924
+ }
925
+
926
+ $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
927
+
928
+ $result = new Math_BigInteger();
929
+ $result->value = $temp[MATH_BIGINTEGER_VALUE];
930
+ $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
931
+
932
+ return $this->_normalize($result);
933
+ }
934
+
935
+ /**
936
+ * Performs subtraction.
937
+ *
938
+ * @param Array $x_value
939
+ * @param Boolean $x_negative
940
+ * @param Array $y_value
941
+ * @param Boolean $y_negative
942
+ * @return Array
943
+ * @access private
944
+ */
945
+ function _subtract($x_value, $x_negative, $y_value, $y_negative)
946
+ {
947
+ $x_size = count($x_value);
948
+ $y_size = count($y_value);
949
+
950
+ if ($x_size == 0) {
951
+ return array(
952
+ MATH_BIGINTEGER_VALUE => $y_value,
953
+ MATH_BIGINTEGER_SIGN => !$y_negative
954
+ );
955
+ } else if ($y_size == 0) {
956
+ return array(
957
+ MATH_BIGINTEGER_VALUE => $x_value,
958
+ MATH_BIGINTEGER_SIGN => $x_negative
959
+ );
960
+ }
961
+
962
+ // add, if appropriate (ie. -$x - +$y or +$x - -$y)
963
+ if ( $x_negative != $y_negative ) {
964
+ $temp = $this->_add($x_value, false, $y_value, false);
965
+ $temp[MATH_BIGINTEGER_SIGN] = $x_negative;
966
+
967
+ return $temp;
968
+ }
969
+
970
+ $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);
971
+
972
+ if ( !$diff ) {
973
+ return array(
974
+ MATH_BIGINTEGER_VALUE => array(),
975
+ MATH_BIGINTEGER_SIGN => false
976
+ );
977
+ }
978
+
979
+ // switch $x and $y around, if appropriate.
980
+ if ( (!$x_negative && $diff < 0) || ($x_negative && $diff > 0) ) {
981
+ $temp = $x_value;
982
+ $x_value = $y_value;
983
+ $y_value = $temp;
984
+
985
+ $x_negative = !$x_negative;
986
+
987
+ $x_size = count($x_value);
988
+ $y_size = count($y_value);
989
+ }
990
+
991
+ // at this point, $x_value should be at least as big as - if not bigger than - $y_value
992
+
993
+ $carry = 0;
994
+ for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
995
+ $sum = $x_value[$j] * 0x4000000 + $x_value[$i] - $y_value[$j] * 0x4000000 - $y_value[$i] - $carry;
996
+ $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
997
+ $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT52 : $sum;
998
+
999
+ $temp = (int) ($sum / 0x4000000);
1000
+
1001
+ $x_value[$i] = (int) ($sum - 0x4000000 * $temp);
1002
+ $x_value[$j] = $temp;
1003
+ }
1004
+
1005
+ if ($j == $y_size) { // ie. if $y_size is odd
1006
+ $sum = $x_value[$i] - $y_value[$i] - $carry;
1007
+ $carry = $sum < 0;
1008
+ $x_value[$i] = $carry ? $sum + 0x4000000 : $sum;
1009
+ ++$i;
1010
+ }
1011
+
1012
+ if ($carry) {
1013
+ for (; !$x_value[$i]; ++$i) {
1014
+ $x_value[$i] = 0x3FFFFFF;
1015
+ }
1016
+ --$x_value[$i];
1017
+ }
1018
+
1019
+ return array(
1020
+ MATH_BIGINTEGER_VALUE => $this->_trim($x_value),
1021
+ MATH_BIGINTEGER_SIGN => $x_negative
1022
+ );
1023
+ }
1024
+
1025
+ /**
1026
+ * Multiplies two BigIntegers
1027
+ *
1028
+ * Here's an example:
1029
+ * <code>
1030
+ * <?php
1031
+ * include('Math/BigInteger.php');
1032
+ *
1033
+ * $a = new Math_BigInteger('10');
1034
+ * $b = new Math_BigInteger('20');
1035
+ *
1036
+ * $c = $a->multiply($b);
1037
+ *
1038
+ * echo $c->toString(); // outputs 200
1039
+ * ?>
1040
+ * </code>
1041
+ *
1042
+ * @param Math_BigInteger $x
1043
+ * @return Math_BigInteger
1044
+ * @access public
1045
+ */
1046
+ function multiply($x)
1047
+ {
1048
+ switch ( MATH_BIGINTEGER_MODE ) {
1049
+ case MATH_BIGINTEGER_MODE_GMP:
1050
+ $temp = new Math_BigInteger();
1051
+ $temp->value = gmp_mul($this->value, $x->value);
1052
+
1053
+ return $this->_normalize($temp);
1054
+ case MATH_BIGINTEGER_MODE_BCMATH:
1055
+ $temp = new Math_BigInteger();
1056
+ $temp->value = bcmul($this->value, $x->value, 0);
1057
+
1058
+ return $this->_normalize($temp);
1059
+ }
1060
+
1061
+ $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
1062
+
1063
+ $product = new Math_BigInteger();
1064
+ $product->value = $temp[MATH_BIGINTEGER_VALUE];
1065
+ $product->is_negative = $temp[MATH_BIGINTEGER_SIGN];
1066
+
1067
+ return $this->_normalize($product);
1068
+ }
1069
+
1070
+ /**
1071
+ * Performs multiplication.
1072
+ *
1073
+ * @param Array $x_value
1074
+ * @param Boolean $x_negative
1075
+ * @param Array $y_value
1076
+ * @param Boolean $y_negative
1077
+ * @return Array
1078
+ * @access private
1079
+ */
1080
+ function _multiply($x_value, $x_negative, $y_value, $y_negative)
1081
+ {
1082
+ //if ( $x_value == $y_value ) {
1083
+ // return array(
1084
+ // MATH_BIGINTEGER_VALUE => $this->_square($x_value),
1085
+ // MATH_BIGINTEGER_SIGN => $x_sign != $y_value
1086
+ // );
1087
+ //}
1088
+
1089
+ $x_length = count($x_value);
1090
+ $y_length = count($y_value);
1091
+
1092
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
1093
+ return array(
1094
+ MATH_BIGINTEGER_VALUE => array(),
1095
+ MATH_BIGINTEGER_SIGN => false
1096
+ );
1097
+ }
1098
+
1099
+ return array(
1100
+ MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
1101
+ $this->_trim($this->_regularMultiply($x_value, $y_value)) :
1102
+ $this->_trim($this->_karatsuba($x_value, $y_value)),
1103
+ MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
1104
+ );
1105
+ }
1106
+
1107
+ /**
1108
+ * Performs long multiplication on two BigIntegers
1109
+ *
1110
+ * Modeled after 'multiply' in MutableBigInteger.java.
1111
+ *
1112
+ * @param Array $x_value
1113
+ * @param Array $y_value
1114
+ * @return Array
1115
+ * @access private
1116
+ */
1117
+ function _regularMultiply($x_value, $y_value)
1118
+ {
1119
+ $x_length = count($x_value);
1120
+ $y_length = count($y_value);
1121
+
1122
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
1123
+ return array();
1124
+ }
1125
+
1126
+ if ( $x_length < $y_length ) {
1127
+ $temp = $x_value;
1128
+ $x_value = $y_value;
1129
+ $y_value = $temp;
1130
+
1131
+ $x_length = count($x_value);
1132
+ $y_length = count($y_value);
1133
+ }
1134
+
1135
+ $product_value = $this->_array_repeat(0, $x_length + $y_length);
1136
+
1137
+ // the following for loop could be removed if the for loop following it
1138
+ // (the one with nested for loops) initially set $i to 0, but
1139
+ // doing so would also make the result in one set of unnecessary adds,
1140
+ // since on the outermost loops first pass, $product->value[$k] is going
1141
+ // to always be 0
1142
+
1143
+ $carry = 0;
1144
+
1145
+ for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
1146
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
1147
+ $carry = (int) ($temp / 0x4000000);
1148
+ $product_value[$j] = (int) ($temp - 0x4000000 * $carry);
1149
+ }
1150
+
1151
+ $product_value[$j] = $carry;
1152
+
1153
+ // the above for loop is what the previous comment was talking about. the
1154
+ // following for loop is the "one with nested for loops"
1155
+ for ($i = 1; $i < $y_length; ++$i) {
1156
+ $carry = 0;
1157
+
1158
+ for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
1159
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
1160
+ $carry = (int) ($temp / 0x4000000);
1161
+ $product_value[$k] = (int) ($temp - 0x4000000 * $carry);
1162
+ }
1163
+
1164
+ $product_value[$k] = $carry;
1165
+ }
1166
+
1167
+ return $product_value;
1168
+ }
1169
+
1170
+ /**
1171
+ * Performs Karatsuba multiplication on two BigIntegers
1172
+ *
1173
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
1174
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
1175
+ *
1176
+ * @param Array $x_value
1177
+ * @param Array $y_value
1178
+ * @return Array
1179
+ * @access private
1180
+ */
1181
+ function _karatsuba($x_value, $y_value)
1182
+ {
1183
+ $m = min(count($x_value) >> 1, count($y_value) >> 1);
1184
+
1185
+ if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
1186
+ return $this->_regularMultiply($x_value, $y_value);
1187
+ }
1188
+
1189
+ $x1 = array_slice($x_value, $m);
1190
+ $x0 = array_slice($x_value, 0, $m);
1191
+ $y1 = array_slice($y_value, $m);
1192
+ $y0 = array_slice($y_value, 0, $m);
1193
+
1194
+ $z2 = $this->_karatsuba($x1, $y1);
1195
+ $z0 = $this->_karatsuba($x0, $y0);
1196
+
1197
+ $z1 = $this->_add($x1, false, $x0, false);
1198
+ $temp = $this->_add($y1, false, $y0, false);
1199
+ $z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]);
1200
+ $temp = $this->_add($z2, false, $z0, false);
1201
+ $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
1202
+
1203
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
1204
+ $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
1205
+
1206
+ $xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
1207
+ $xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false);
1208
+
1209
+ return $xy[MATH_BIGINTEGER_VALUE];
1210
+ }
1211
+
1212
+ /**
1213
+ * Performs squaring
1214
+ *
1215
+ * @param Array $x
1216
+ * @return Array
1217
+ * @access private
1218
+ */
1219
+ function _square($x = false)
1220
+ {
1221
+ return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
1222
+ $this->_trim($this->_baseSquare($x)) :
1223
+ $this->_trim($this->_karatsubaSquare($x));
1224
+ }
1225
+
1226
+ /**
1227
+ * Performs traditional squaring on two BigIntegers
1228
+ *
1229
+ * Squaring can be done faster than multiplying a number by itself can be. See
1230
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
1231
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
1232
+ *
1233
+ * @param Array $value
1234
+ * @return Array
1235
+ * @access private
1236
+ */
1237
+ function _baseSquare($value)
1238
+ {
1239
+ if ( empty($value) ) {
1240
+ return array();
1241
+ }
1242
+ $square_value = $this->_array_repeat(0, 2 * count($value));
1243
+
1244
+ for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
1245
+ $i2 = $i << 1;
1246
+
1247
+ $temp = $square_value[$i2] + $value[$i] * $value[$i];
1248
+ $carry = (int) ($temp / 0x4000000);
1249
+ $square_value[$i2] = (int) ($temp - 0x4000000 * $carry);
1250
+
1251
+ // note how we start from $i+1 instead of 0 as we do in multiplication.
1252
+ for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
1253
+ $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
1254
+ $carry = (int) ($temp / 0x4000000);
1255
+ $square_value[$k] = (int) ($temp - 0x4000000 * $carry);
1256
+ }
1257
+
1258
+ // the following line can yield values larger 2**15. at this point, PHP should switch
1259
+ // over to floats.
1260
+ $square_value[$i + $max_index + 1] = $carry;
1261
+ }
1262
+
1263
+ return $square_value;
1264
+ }
1265
+
1266
+ /**
1267
+ * Performs Karatsuba "squaring" on two BigIntegers
1268
+ *
1269
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
1270
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
1271
+ *
1272
+ * @param Array $value
1273
+ * @return Array
1274
+ * @access private
1275
+ */
1276
+ function _karatsubaSquare($value)
1277
+ {
1278
+ $m = count($value) >> 1;
1279
+
1280
+ if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
1281
+ return $this->_baseSquare($value);
1282
+ }
1283
+
1284
+ $x1 = array_slice($value, $m);
1285
+ $x0 = array_slice($value, 0, $m);
1286
+
1287
+ $z2 = $this->_karatsubaSquare($x1);
1288
+ $z0 = $this->_karatsubaSquare($x0);
1289
+
1290
+ $z1 = $this->_add($x1, false, $x0, false);
1291
+ $z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]);
1292
+ $temp = $this->_add($z2, false, $z0, false);
1293
+ $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
1294
+
1295
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
1296
+ $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
1297
+
1298
+ $xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
1299
+ $xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false);
1300
+
1301
+ return $xx[MATH_BIGINTEGER_VALUE];
1302
+ }
1303
+
1304
+ /**
1305
+ * Divides two BigIntegers.
1306
+ *
1307
+ * Returns an array whose first element contains the quotient and whose second element contains the
1308
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
1309
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
1310
+ * and the divisor (basically, the "common residue" is the first positive modulo).
1311
+ *
1312
+ * Here's an example:
1313
+ * <code>
1314
+ * <?php
1315
+ * include('Math/BigInteger.php');
1316
+ *
1317
+ * $a = new Math_BigInteger('10');
1318
+ * $b = new Math_BigInteger('20');
1319
+ *
1320
+ * list($quotient, $remainder) = $a->divide($b);
1321
+ *
1322
+ * echo $quotient->toString(); // outputs 0
1323
+ * echo "\r\n";
1324
+ * echo $remainder->toString(); // outputs 10
1325
+ * ?>
1326
+ * </code>
1327
+ *
1328
+ * @param Math_BigInteger $y
1329
+ * @return Array
1330
+ * @access public
1331
+ * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
1332
+ */
1333
+ function divide($y)
1334
+ {
1335
+ switch ( MATH_BIGINTEGER_MODE ) {
1336
+ case MATH_BIGINTEGER_MODE_GMP:
1337
+ $quotient = new Math_BigInteger();
1338
+ $remainder = new Math_BigInteger();
1339
+
1340
+ list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
1341
+
1342
+ if (gmp_sign($remainder->value) < 0) {
1343
+ $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
1344
+ }
1345
+
1346
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
1347
+ case MATH_BIGINTEGER_MODE_BCMATH:
1348
+ $quotient = new Math_BigInteger();
1349
+ $remainder = new Math_BigInteger();
1350
+
1351
+ $quotient->value = bcdiv($this->value, $y->value, 0);
1352
+ $remainder->value = bcmod($this->value, $y->value);
1353
+
1354
+ if ($remainder->value[0] == '-') {
1355
+ $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
1356
+ }
1357
+
1358
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
1359
+ }
1360
+
1361
+ if (count($y->value) == 1) {
1362
+ list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
1363
+ $quotient = new Math_BigInteger();
1364
+ $remainder = new Math_BigInteger();
1365
+ $quotient->value = $q;
1366
+ $remainder->value = array($r);
1367
+ $quotient->is_negative = $this->is_negative != $y->is_negative;
1368
+ return array($this->_normalize($quotient), $this->_normalize($remainder));
1369
+ }
1370
+
1371
+ static $zero;
1372
+ if ( !isset($zero) ) {
1373
+ $zero = new Math_BigInteger();
1374
+ }
1375
+
1376
+ $x = $this->copy();
1377
+ $y = $y->copy();
1378
+
1379
+ $x_sign = $x->is_negative;
1380
+ $y_sign = $y->is_negative;
1381
+
1382
+ $x->is_negative = $y->is_negative = false;
1383
+
1384
+ $diff = $x->compare($y);
1385
+
1386
+ if ( !$diff ) {
1387
+ $temp = new Math_BigInteger();
1388
+ $temp->value = array(1);
1389
+ $temp->is_negative = $x_sign != $y_sign;
1390
+ return array($this->_normalize($temp), $this->_normalize(new Math_BigInteger()));
1391
+ }
1392
+
1393
+ if ( $diff < 0 ) {
1394
+ // if $x is negative, "add" $y.
1395
+ if ( $x_sign ) {
1396
+ $x = $y->subtract($x);
1397
+ }
1398
+ return array($this->_normalize(new Math_BigInteger()), $this->_normalize($x));
1399
+ }
1400
+
1401
+ // normalize $x and $y as described in HAC 14.23 / 14.24
1402
+ $msb = $y->value[count($y->value) - 1];
1403
+ for ($shift = 0; !($msb & 0x2000000); ++$shift) {
1404
+ $msb <<= 1;
1405
+ }
1406
+ $x->_lshift($shift);
1407
+ $y->_lshift($shift);
1408
+ $y_value = &$y->value;
1409
+
1410
+ $x_max = count($x->value) - 1;
1411
+ $y_max = count($y->value) - 1;
1412
+
1413
+ $quotient = new Math_BigInteger();
1414
+ $quotient_value = &$quotient->value;
1415
+ $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
1416
+
1417
+ static $temp, $lhs, $rhs;
1418
+ if (!isset($temp)) {
1419
+ $temp = new Math_BigInteger();
1420
+ $lhs = new Math_BigInteger();
1421
+ $rhs = new Math_BigInteger();
1422
+ }
1423
+ $temp_value = &$temp->value;
1424
+ $rhs_value = &$rhs->value;
1425
+
1426
+ // $temp = $y << ($x_max - $y_max-1) in base 2**26
1427
+ $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
1428
+
1429
+ while ( $x->compare($temp) >= 0 ) {
1430
+ // calculate the "common residue"
1431
+ ++$quotient_value[$x_max - $y_max];
1432
+ $x = $x->subtract($temp);
1433
+ $x_max = count($x->value) - 1;
1434
+ }
1435
+
1436
+ for ($i = $x_max; $i >= $y_max + 1; --$i) {
1437
+ $x_value = &$x->value;
1438
+ $x_window = array(
1439
+ isset($x_value[$i]) ? $x_value[$i] : 0,
1440
+ isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
1441
+ isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
1442
+ );
1443
+ $y_window = array(
1444
+ $y_value[$y_max],
1445
+ ( $y_max > 0 ) ? $y_value[$y_max - 1] : 0
1446
+ );
1447
+
1448
+ $q_index = $i - $y_max - 1;
1449
+ if ($x_window[0] == $y_window[0]) {
1450
+ $quotient_value[$q_index] = 0x3FFFFFF;
1451
+ } else {
1452
+ $quotient_value[$q_index] = (int) (
1453
+ ($x_window[0] * 0x4000000 + $x_window[1])
1454
+ /
1455
+ $y_window[0]
1456
+ );
1457
+ }
1458
+
1459
+ $temp_value = array($y_window[1], $y_window[0]);
1460
+
1461
+ $lhs->value = array($quotient_value[$q_index]);
1462
+ $lhs = $lhs->multiply($temp);
1463
+
1464
+ $rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
1465
+
1466
+ while ( $lhs->compare($rhs) > 0 ) {
1467
+ --$quotient_value[$q_index];
1468
+
1469
+ $lhs->value = array($quotient_value[$q_index]);
1470
+ $lhs = $lhs->multiply($temp);
1471
+ }
1472
+
1473
+ $adjust = $this->_array_repeat(0, $q_index);
1474
+ $temp_value = array($quotient_value[$q_index]);
1475
+ $temp = $temp->multiply($y);
1476
+ $temp_value = &$temp->value;
1477
+ $temp_value = array_merge($adjust, $temp_value);
1478
+
1479
+ $x = $x->subtract($temp);
1480
+
1481
+ if ($x->compare($zero) < 0) {
1482
+ $temp_value = array_merge($adjust, $y_value);
1483
+ $x = $x->add($temp);
1484
+
1485
+ --$quotient_value[$q_index];
1486
+ }
1487
+
1488
+ $x_max = count($x_value) - 1;
1489
+ }
1490
+
1491
+ // unnormalize the remainder
1492
+ $x->_rshift($shift);
1493
+
1494
+ $quotient->is_negative = $x_sign != $y_sign;
1495
+
1496
+ // calculate the "common residue", if appropriate
1497
+ if ( $x_sign ) {
1498
+ $y->_rshift($shift);
1499
+ $x = $y->subtract($x);
1500
+ }
1501
+
1502
+ return array($this->_normalize($quotient), $this->_normalize($x));
1503
+ }
1504
+
1505
+ /**
1506
+ * Divides a BigInteger by a regular integer
1507
+ *
1508
+ * abc / x = a00 / x + b0 / x + c / x
1509
+ *
1510
+ * @param Array $dividend
1511
+ * @param Array $divisor
1512
+ * @return Array
1513
+ * @access private
1514
+ */
1515
+ function _divide_digit($dividend, $divisor)
1516
+ {
1517
+ $carry = 0;
1518
+ $result = array();
1519
+
1520
+ for ($i = count($dividend) - 1; $i >= 0; --$i) {
1521
+ $temp = 0x4000000 * $carry + $dividend[$i];
1522
+ $result[$i] = (int) ($temp / $divisor);
1523
+ $carry = (int) ($temp - $divisor * $result[$i]);
1524
+ }
1525
+
1526
+ return array($result, $carry);
1527
+ }
1528
+
1529
+ /**
1530
+ * Performs modular exponentiation.
1531
+ *
1532
+ * Here's an example:
1533
+ * <code>
1534
+ * <?php
1535
+ * include('Math/BigInteger.php');
1536
+ *
1537
+ * $a = new Math_BigInteger('10');
1538
+ * $b = new Math_BigInteger('20');
1539
+ * $c = new Math_BigInteger('30');
1540
+ *
1541
+ * $c = $a->modPow($b, $c);
1542
+ *
1543
+ * echo $c->toString(); // outputs 10
1544
+ * ?>
1545
+ * </code>
1546
+ *
1547
+ * @param Math_BigInteger $e
1548
+ * @param Math_BigInteger $n
1549
+ * @return Math_BigInteger
1550
+ * @access public
1551
+ * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
1552
+ * and although the approach involving repeated squaring does vastly better, it, too, is impractical
1553
+ * for our purposes. The reason being that division - by far the most complicated and time-consuming
1554
+ * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
1555
+ *
1556
+ * Modular reductions resolve this issue. Although an individual modular reduction takes more time
1557
+ * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
1558
+ *
1559
+ * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
1560
+ * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
1561
+ * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
1562
+ * the product of two odd numbers is odd), but what about when RSA isn't used?
1563
+ *
1564
+ * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
1565
+ * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
1566
+ * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
1567
+ * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
1568
+ * the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
1569
+ * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
1570
+ */
1571
+ function modPow($e, $n)
1572
+ {
1573
+ $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
1574
+
1575
+ if ($e->compare(new Math_BigInteger()) < 0) {
1576
+ $e = $e->abs();
1577
+
1578
+ $temp = $this->modInverse($n);
1579
+ if ($temp === false) {
1580
+ return false;
1581
+ }
1582
+
1583
+ return $this->_normalize($temp->modPow($e, $n));
1584
+ }
1585
+
1586
+ switch ( MATH_BIGINTEGER_MODE ) {
1587
+ case MATH_BIGINTEGER_MODE_GMP:
1588
+ $temp = new Math_BigInteger();
1589
+ $temp->value = gmp_powm($this->value, $e->value, $n->value);
1590
+
1591
+ return $this->_normalize($temp);
1592
+ case MATH_BIGINTEGER_MODE_BCMATH:
1593
+ $temp = new Math_BigInteger();
1594
+ $temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
1595
+
1596
+ return $this->_normalize($temp);
1597
+ }
1598
+
1599
+ if ( empty($e->value) ) {
1600
+ $temp = new Math_BigInteger();
1601
+ $temp->value = array(1);
1602
+ return $this->_normalize($temp);
1603
+ }
1604
+
1605
+ if ( $e->value == array(1) ) {
1606
+ list(, $temp) = $this->divide($n);
1607
+ return $this->_normalize($temp);
1608
+ }
1609
+
1610
+ if ( $e->value == array(2) ) {
1611
+ $temp = new Math_BigInteger();
1612
+ $temp->value = $this->_square($this->value);
1613
+ list(, $temp) = $temp->divide($n);
1614
+ return $this->_normalize($temp);
1615
+ }
1616
+
1617
+ return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT));
1618
+
1619
+ // is the modulo odd?
1620
+ if ( $n->value[0] & 1 ) {
1621
+ return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY));
1622
+ }
1623
+ // if it's not, it's even
1624
+
1625
+ // find the lowest set bit (eg. the max pow of 2 that divides $n)
1626
+ for ($i = 0; $i < count($n->value); ++$i) {
1627
+ if ( $n->value[$i] ) {
1628
+ $temp = decbin($n->value[$i]);
1629
+ $j = strlen($temp) - strrpos($temp, '1') - 1;
1630
+ $j+= 26 * $i;
1631
+ break;
1632
+ }
1633
+ }
1634
+ // at this point, 2^$j * $n/(2^$j) == $n
1635
+
1636
+ $mod1 = $n->copy();
1637
+ $mod1->_rshift($j);
1638
+ $mod2 = new Math_BigInteger();
1639
+ $mod2->value = array(1);
1640
+ $mod2->_lshift($j);
1641
+
1642
+ $part1 = ( $mod1->value != array(1) ) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new Math_BigInteger();
1643
+ $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2);
1644
+
1645
+ $y1 = $mod2->modInverse($mod1);
1646
+ $y2 = $mod1->modInverse($mod2);
1647
+
1648
+ $result = $part1->multiply($mod2);
1649
+ $result = $result->multiply($y1);
1650
+
1651
+ $temp = $part2->multiply($mod1);
1652
+ $temp = $temp->multiply($y2);
1653
+
1654
+ $result = $result->add($temp);
1655
+ list(, $result) = $result->divide($n);
1656
+
1657
+ return $this->_normalize($result);
1658
+ }
1659
+
1660
+ /**
1661
+ * Performs modular exponentiation.
1662
+ *
1663
+ * Alias for Math_BigInteger::modPow()
1664
+ *
1665
+ * @param Math_BigInteger $e
1666
+ * @param Math_BigInteger $n
1667
+ * @return Math_BigInteger
1668
+ * @access public
1669
+ */
1670
+ function powMod($e, $n)
1671
+ {
1672
+ return $this->modPow($e, $n);
1673
+ }
1674
+
1675
+ /**
1676
+ * Sliding Window k-ary Modular Exponentiation
1677
+ *
1678
+ * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
1679
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
1680
+ * however, this function performs a modular reduction after every multiplication and squaring operation.
1681
+ * As such, this function has the same preconditions that the reductions being used do.
1682
+ *
1683
+ * @param Math_BigInteger $e
1684
+ * @param Math_BigInteger $n
1685
+ * @param Integer $mode
1686
+ * @return Math_BigInteger
1687
+ * @access private
1688
+ */
1689
+ function _slidingWindow($e, $n, $mode)
1690
+ {
1691
+ static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function
1692
+ //static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1
1693
+
1694
+ $e_value = $e->value;
1695
+ $e_length = count($e_value) - 1;
1696
+ $e_bits = decbin($e_value[$e_length]);
1697
+ for ($i = $e_length - 1; $i >= 0; --$i) {
1698
+ $e_bits.= str_pad(decbin($e_value[$i]), 26, '0', STR_PAD_LEFT);
1699
+ }
1700
+
1701
+ $e_length = strlen($e_bits);
1702
+
1703
+ // calculate the appropriate window size.
1704
+ // $window_size == 3 if $window_ranges is between 25 and 81, for example.
1705
+ for ($i = 0, $window_size = 1; $e_length > $window_ranges[$i] && $i < count($window_ranges); ++$window_size, ++$i);
1706
+
1707
+ $n_value = $n->value;
1708
+
1709
+ // precompute $this^0 through $this^$window_size
1710
+ $powers = array();
1711
+ $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
1712
+ $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
1713
+
1714
+ // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
1715
+ // in a 1. ie. it's supposed to be odd.
1716
+ $temp = 1 << ($window_size - 1);
1717
+ for ($i = 1; $i < $temp; ++$i) {
1718
+ $i2 = $i << 1;
1719
+ $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
1720
+ }
1721
+
1722
+ $result = array(1);
1723
+ $result = $this->_prepareReduce($result, $n_value, $mode);
1724
+
1725
+ for ($i = 0; $i < $e_length; ) {
1726
+ if ( !$e_bits[$i] ) {
1727
+ $result = $this->_squareReduce($result, $n_value, $mode);
1728
+ ++$i;
1729
+ } else {
1730
+ for ($j = $window_size - 1; $j > 0; --$j) {
1731
+ if ( !empty($e_bits[$i + $j]) ) {
1732
+ break;
1733
+ }
1734
+ }
1735
+
1736
+ for ($k = 0; $k <= $j; ++$k) {// eg. the length of substr($e_bits, $i, $j+1)
1737
+ $result = $this->_squareReduce($result, $n_value, $mode);
1738
+ }
1739
+
1740
+ $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
1741
+
1742
+ $i+=$j + 1;
1743
+ }
1744
+ }
1745
+
1746
+ $temp = new Math_BigInteger();
1747
+ $temp->value = $this->_reduce($result, $n_value, $mode);
1748
+
1749
+ return $temp;
1750
+ }
1751
+
1752
+ /**
1753
+ * Modular reduction
1754
+ *
1755
+ * For most $modes this will return the remainder.
1756
+ *
1757
+ * @see _slidingWindow()
1758
+ * @access private
1759
+ * @param Array $x
1760
+ * @param Array $n
1761
+ * @param Integer $mode
1762
+ * @return Array
1763
+ */
1764
+ function _reduce($x, $n, $mode)
1765
+ {
1766
+ switch ($mode) {
1767
+ case MATH_BIGINTEGER_MONTGOMERY:
1768
+ return $this->_montgomery($x, $n);
1769
+ case MATH_BIGINTEGER_BARRETT:
1770
+ return $this->_barrett($x, $n);
1771
+ case MATH_BIGINTEGER_POWEROF2:
1772
+ $lhs = new Math_BigInteger();
1773
+ $lhs->value = $x;
1774
+ $rhs = new Math_BigInteger();
1775
+ $rhs->value = $n;
1776
+ return $x->_mod2($n);
1777
+ case MATH_BIGINTEGER_CLASSIC:
1778
+ $lhs = new Math_BigInteger();
1779
+ $lhs->value = $x;
1780
+ $rhs = new Math_BigInteger();
1781
+ $rhs->value = $n;
1782
+ list(, $temp) = $lhs->divide($rhs);
1783
+ return $temp->value;
1784
+ case MATH_BIGINTEGER_NONE:
1785
+ return $x;
1786
+ default:
1787
+ // an invalid $mode was provided
1788
+ }
1789
+ }
1790
+
1791
+ /**
1792
+ * Modular reduction preperation
1793
+ *
1794
+ * @see _slidingWindow()
1795
+ * @access private
1796
+ * @param Array $x
1797
+ * @param Array $n
1798
+ * @param Integer $mode
1799
+ * @return Array
1800
+ */
1801
+ function _prepareReduce($x, $n, $mode)
1802
+ {
1803
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
1804
+ return $this->_prepMontgomery($x, $n);
1805
+ }
1806
+ return $this->_reduce($x, $n, $mode);
1807
+ }
1808
+
1809
+ /**
1810
+ * Modular multiply
1811
+ *
1812
+ * @see _slidingWindow()
1813
+ * @access private
1814
+ * @param Array $x
1815
+ * @param Array $y
1816
+ * @param Array $n
1817
+ * @param Integer $mode
1818
+ * @return Array
1819
+ */
1820
+ function _multiplyReduce($x, $y, $n, $mode)
1821
+ {
1822
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
1823
+ return $this->_montgomeryMultiply($x, $y, $n);
1824
+ }
1825
+ $temp = $this->_multiply($x, false, $y, false);
1826
+ return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode);
1827
+ }
1828
+
1829
+ /**
1830
+ * Modular square
1831
+ *
1832
+ * @see _slidingWindow()
1833
+ * @access private
1834
+ * @param Array $x
1835
+ * @param Array $n
1836
+ * @param Integer $mode
1837
+ * @return Array
1838
+ */
1839
+ function _squareReduce($x, $n, $mode)
1840
+ {
1841
+ if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
1842
+ return $this->_montgomeryMultiply($x, $x, $n);
1843
+ }
1844
+ return $this->_reduce($this->_square($x), $n, $mode);
1845
+ }
1846
+
1847
+ /**
1848
+ * Modulos for Powers of Two
1849
+ *
1850
+ * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1),
1851
+ * we'll just use this function as a wrapper for doing that.
1852
+ *
1853
+ * @see _slidingWindow()
1854
+ * @access private
1855
+ * @param Math_BigInteger
1856
+ * @return Math_BigInteger
1857
+ */
1858
+ function _mod2($n)
1859
+ {
1860
+ $temp = new Math_BigInteger();
1861
+ $temp->value = array(1);
1862
+ return $this->bitwise_and($n->subtract($temp));
1863
+ }
1864
+
1865
+ /**
1866
+ * Barrett Modular Reduction
1867
+ *
1868
+ * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
1869
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
1870
+ * so as not to require negative numbers (initially, this script didn't support negative numbers).
1871
+ *
1872
+ * Employs "folding", as described at
1873
+ * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
1874
+ * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
1875
+ *
1876
+ * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
1877
+ * usable on account of (1) its not using reasonable radix points as discussed in
1878
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
1879
+ * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
1880
+ * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
1881
+ * comments for details.
1882
+ *
1883
+ * @see _slidingWindow()
1884
+ * @access private
1885
+ * @param Array $n
1886
+ * @param Array $m
1887
+ * @return Array
1888
+ */
1889
+ function _barrett($n, $m)
1890
+ {
1891
+ static $cache = array(
1892
+ MATH_BIGINTEGER_VARIABLE => array(),
1893
+ MATH_BIGINTEGER_DATA => array()
1894
+ );
1895
+
1896
+ $m_length = count($m);
1897
+
1898
+ // if ($this->_compare($n, $this->_square($m)) >= 0) {
1899
+ if (count($n) > 2 * $m_length) {
1900
+ $lhs = new Math_BigInteger();
1901
+ $rhs = new Math_BigInteger();
1902
+ $lhs->value = $n;
1903
+ $rhs->value = $m;
1904
+ list(, $temp) = $lhs->divide($rhs);
1905
+ return $temp->value;
1906
+ }
1907
+
1908
+ // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
1909
+ if ($m_length < 5) {
1910
+ return $this->_regularBarrett($n, $m);
1911
+ }
1912
+
1913
+ // n = 2 * m.length
1914
+
1915
+ if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
1916
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
1917
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
1918
+
1919
+ $lhs = new Math_BigInteger();
1920
+ $lhs_value = &$lhs->value;
1921
+ $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
1922
+ $lhs_value[] = 1;
1923
+ $rhs = new Math_BigInteger();
1924
+ $rhs->value = $m;
1925
+
1926
+ list($u, $m1) = $lhs->divide($rhs);
1927
+ $u = $u->value;
1928
+ $m1 = $m1->value;
1929
+
1930
+ $cache[MATH_BIGINTEGER_DATA][] = array(
1931
+ 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
1932
+ 'm1'=> $m1 // m.length
1933
+ );
1934
+ } else {
1935
+ extract($cache[MATH_BIGINTEGER_DATA][$key]);
1936
+ }
1937
+
1938
+ $cutoff = $m_length + ($m_length >> 1);
1939
+ $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
1940
+ $msd = array_slice($n, $cutoff); // m.length >> 1
1941
+ $lsd = $this->_trim($lsd);
1942
+ $temp = $this->_multiply($msd, false, $m1, false);
1943
+ $n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1
1944
+
1945
+ if ($m_length & 1) {
1946
+ return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m);
1947
+ }
1948
+
1949
+ // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
1950
+ $temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1);
1951
+ // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
1952
+ // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
1953
+ $temp = $this->_multiply($temp, false, $u, false);
1954
+ // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
1955
+ // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
1956
+ $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1);
1957
+ // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
1958
+ // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
1959
+ $temp = $this->_multiply($temp, false, $m, false);
1960
+
1961
+ // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
1962
+ // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
1963
+ // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
1964
+
1965
+ $result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
1966
+
1967
+ while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) {
1968
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false);
1969
+ }
1970
+
1971
+ return $result[MATH_BIGINTEGER_VALUE];
1972
+ }
1973
+
1974
+ /**
1975
+ * (Regular) Barrett Modular Reduction
1976
+ *
1977
+ * For numbers with more than four digits Math_BigInteger::_barrett() is faster. The difference between that and this
1978
+ * is that this function does not fold the denominator into a smaller form.
1979
+ *
1980
+ * @see _slidingWindow()
1981
+ * @access private
1982
+ * @param Array $x
1983
+ * @param Array $n
1984
+ * @return Array
1985
+ */
1986
+ function _regularBarrett($x, $n)
1987
+ {
1988
+ static $cache = array(
1989
+ MATH_BIGINTEGER_VARIABLE => array(),
1990
+ MATH_BIGINTEGER_DATA => array()
1991
+ );
1992
+
1993
+ $n_length = count($n);
1994
+
1995
+ if (count($x) > 2 * $n_length) {
1996
+ $lhs = new Math_BigInteger();
1997
+ $rhs = new Math_BigInteger();
1998
+ $lhs->value = $x;
1999
+ $rhs->value = $n;
2000
+ list(, $temp) = $lhs->divide($rhs);
2001
+ return $temp->value;
2002
+ }
2003
+
2004
+ if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
2005
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
2006
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $n;
2007
+ $lhs = new Math_BigInteger();
2008
+ $lhs_value = &$lhs->value;
2009
+ $lhs_value = $this->_array_repeat(0, 2 * $n_length);
2010
+ $lhs_value[] = 1;
2011
+ $rhs = new Math_BigInteger();
2012
+ $rhs->value = $n;
2013
+ list($temp, ) = $lhs->divide($rhs); // m.length
2014
+ $cache[MATH_BIGINTEGER_DATA][] = $temp->value;
2015
+ }
2016
+
2017
+ // 2 * m.length - (m.length - 1) = m.length + 1
2018
+ $temp = array_slice($x, $n_length - 1);
2019
+ // (m.length + 1) + m.length = 2 * m.length + 1
2020
+ $temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false);
2021
+ // (2 * m.length + 1) - (m.length - 1) = m.length + 2
2022
+ $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1);
2023
+
2024
+ // m.length + 1
2025
+ $result = array_slice($x, 0, $n_length + 1);
2026
+ // m.length + 1
2027
+ $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
2028
+ // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1)
2029
+
2030
+ if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) {
2031
+ $corrector_value = $this->_array_repeat(0, $n_length + 1);
2032
+ $corrector_value[] = 1;
2033
+ $result = $this->_add($result, false, $corrector, false);
2034
+ $result = $result[MATH_BIGINTEGER_VALUE];
2035
+ }
2036
+
2037
+ // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
2038
+ $result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]);
2039
+ while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) {
2040
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false);
2041
+ }
2042
+
2043
+ return $result[MATH_BIGINTEGER_VALUE];
2044
+ }
2045
+
2046
+ /**
2047
+ * Performs long multiplication up to $stop digits
2048
+ *
2049
+ * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
2050
+ *
2051
+ * @see _regularBarrett()
2052
+ * @param Array $x_value
2053
+ * @param Boolean $x_negative
2054
+ * @param Array $y_value
2055
+ * @param Boolean $y_negative
2056
+ * @return Array
2057
+ * @access private
2058
+ */
2059
+ function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
2060
+ {
2061
+ $x_length = count($x_value);
2062
+ $y_length = count($y_value);
2063
+
2064
+ if ( !$x_length || !$y_length ) { // a 0 is being multiplied
2065
+ return array(
2066
+ MATH_BIGINTEGER_VALUE => array(),
2067
+ MATH_BIGINTEGER_SIGN => false
2068
+ );
2069
+ }
2070
+
2071
+ if ( $x_length < $y_length ) {
2072
+ $temp = $x_value;
2073
+ $x_value = $y_value;
2074
+ $y_value = $temp;
2075
+
2076
+ $x_length = count($x_value);
2077
+ $y_length = count($y_value);
2078
+ }
2079
+
2080
+ $product_value = $this->_array_repeat(0, $x_length + $y_length);
2081
+
2082
+ // the following for loop could be removed if the for loop following it
2083
+ // (the one with nested for loops) initially set $i to 0, but
2084
+ // doing so would also make the result in one set of unnecessary adds,
2085
+ // since on the outermost loops first pass, $product->value[$k] is going
2086
+ // to always be 0
2087
+
2088
+ $carry = 0;
2089
+
2090
+ for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
2091
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
2092
+ $carry = (int) ($temp / 0x4000000);
2093
+ $product_value[$j] = (int) ($temp - 0x4000000 * $carry);
2094
+ }
2095
+
2096
+ if ($j < $stop) {
2097
+ $product_value[$j] = $carry;
2098
+ }
2099
+
2100
+ // the above for loop is what the previous comment was talking about. the
2101
+ // following for loop is the "one with nested for loops"
2102
+
2103
+ for ($i = 1; $i < $y_length; ++$i) {
2104
+ $carry = 0;
2105
+
2106
+ for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
2107
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
2108
+ $carry = (int) ($temp / 0x4000000);
2109
+ $product_value[$k] = (int) ($temp - 0x4000000 * $carry);
2110
+ }
2111
+
2112
+ if ($k < $stop) {
2113
+ $product_value[$k] = $carry;
2114
+ }
2115
+ }
2116
+
2117
+ return array(
2118
+ MATH_BIGINTEGER_VALUE => $this->_trim($product_value),
2119
+ MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
2120
+ );
2121
+ }
2122
+
2123
+ /**
2124
+ * Montgomery Modular Reduction
2125
+ *
2126
+ * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
2127
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
2128
+ * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function
2129
+ * to work correctly.
2130
+ *
2131
+ * @see _prepMontgomery()
2132
+ * @see _slidingWindow()
2133
+ * @access private
2134
+ * @param Array $x
2135
+ * @param Array $n
2136
+ * @return Array
2137
+ */
2138
+ function _montgomery($x, $n)
2139
+ {
2140
+ static $cache = array(
2141
+ MATH_BIGINTEGER_VARIABLE => array(),
2142
+ MATH_BIGINTEGER_DATA => array()
2143
+ );
2144
+
2145
+ if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
2146
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
2147
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $x;
2148
+ $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n);
2149
+ }
2150
+
2151
+ $k = count($n);
2152
+
2153
+ $result = array(MATH_BIGINTEGER_VALUE => $x);
2154
+
2155
+ for ($i = 0; $i < $k; ++$i) {
2156
+ $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key];
2157
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
2158
+ $temp = $this->_regularMultiply(array($temp), $n);
2159
+ $temp = array_merge($this->_array_repeat(0, $i), $temp);
2160
+ $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false);
2161
+ }
2162
+
2163
+ $result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k);
2164
+
2165
+ if ($this->_compare($result, false, $n, false) >= 0) {
2166
+ $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false);
2167
+ }
2168
+
2169
+ return $result[MATH_BIGINTEGER_VALUE];
2170
+ }
2171
+
2172
+ /**
2173
+ * Montgomery Multiply
2174
+ *
2175
+ * Interleaves the montgomery reduction and long multiplication algorithms together as described in
2176
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
2177
+ *
2178
+ * @see _prepMontgomery()
2179
+ * @see _montgomery()
2180
+ * @access private
2181
+ * @param Array $x
2182
+ * @param Array $y
2183
+ * @param Array $m
2184
+ * @return Array
2185
+ */
2186
+ function _montgomeryMultiply($x, $y, $m)
2187
+ {
2188
+ $temp = $this->_multiply($x, false, $y, false);
2189
+ return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m);
2190
+
2191
+ static $cache = array(
2192
+ MATH_BIGINTEGER_VARIABLE => array(),
2193
+ MATH_BIGINTEGER_DATA => array()
2194
+ );
2195
+
2196
+ if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
2197
+ $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
2198
+ $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
2199
+ $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m);
2200
+ }
2201
+
2202
+ $n = max(count($x), count($y), count($m));
2203
+ $x = array_pad($x, $n, 0);
2204
+ $y = array_pad($y, $n, 0);
2205
+ $m = array_pad($m, $n, 0);
2206
+ $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1));
2207
+ for ($i = 0; $i < $n; ++$i) {
2208
+ $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0];
2209
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
2210
+ $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key];
2211
+ $temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
2212
+ $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
2213
+ $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
2214
+ $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1);
2215
+ }
2216
+ if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) {
2217
+ $a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false);
2218
+ }
2219
+ return $a[MATH_BIGINTEGER_VALUE];
2220
+ }
2221
+
2222
+ /**
2223
+ * Prepare a number for use in Montgomery Modular Reductions
2224
+ *
2225
+ * @see _montgomery()
2226
+ * @see _slidingWindow()
2227
+ * @access private
2228
+ * @param Array $x
2229
+ * @param Array $n
2230
+ * @return Array
2231
+ */
2232
+ function _prepMontgomery($x, $n)
2233
+ {
2234
+ $lhs = new Math_BigInteger();
2235
+ $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
2236
+ $rhs = new Math_BigInteger();
2237
+ $rhs->value = $n;
2238
+
2239
+ list(, $temp) = $lhs->divide($rhs);
2240
+ return $temp->value;
2241
+ }
2242
+
2243
+ /**
2244
+ * Modular Inverse of a number mod 2**26 (eg. 67108864)
2245
+ *
2246
+ * Based off of the bnpInvDigit function implemented and justified in the following URL:
2247
+ *
2248
+ * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
2249
+ *
2250
+ * The following URL provides more info:
2251
+ *
2252
+ * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
2253
+ *
2254
+ * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
2255
+ * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
2256
+ * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
2257
+ * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
2258
+ * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
2259
+ * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
2260
+ * 40 bits, which only 64-bit floating points will support.
2261
+ *
2262
+ * Thanks to Pedro Gimeno Fortea for input!
2263
+ *
2264
+ * @see _montgomery()
2265
+ * @access private
2266
+ * @param Array $x
2267
+ * @return Integer
2268
+ */
2269
+ function _modInverse67108864($x) // 2**26 == 67108864
2270
+ {
2271
+ $x = -$x[0];
2272
+ $result = $x & 0x3; // x**-1 mod 2**2
2273
+ $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
2274
+ $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
2275
+ $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
2276
+ $result = fmod($result * (2 - fmod($x * $result, 0x4000000)), 0x4000000); // x**-1 mod 2**26
2277
+ return $result & 0x3FFFFFF;
2278
+ }
2279
+
2280
+ /**
2281
+ * Calculates modular inverses.
2282
+ *
2283
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
2284
+ *
2285
+ * Here's an example:
2286
+ * <code>
2287
+ * <?php
2288
+ * include('Math/BigInteger.php');
2289
+ *
2290
+ * $a = new Math_BigInteger(30);
2291
+ * $b = new Math_BigInteger(17);
2292
+ *
2293
+ * $c = $a->modInverse($b);
2294
+ * echo $c->toString(); // outputs 4
2295
+ *
2296
+ * echo "\r\n";
2297
+ *
2298
+ * $d = $a->multiply($c);
2299
+ * list(, $d) = $d->divide($b);
2300
+ * echo $d; // outputs 1 (as per the definition of modular inverse)
2301
+ * ?>
2302
+ * </code>
2303
+ *
2304
+ * @param Math_BigInteger $n
2305
+ * @return mixed false, if no modular inverse exists, Math_BigInteger, otherwise.
2306
+ * @access public
2307
+ * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
2308
+ */
2309
+ function modInverse($n)
2310
+ {
2311
+ switch ( MATH_BIGINTEGER_MODE ) {
2312
+ case MATH_BIGINTEGER_MODE_GMP:
2313
+ $temp = new Math_BigInteger();
2314
+ $temp->value = gmp_invert($this->value, $n->value);
2315
+
2316
+ return ( $temp->value === false ) ? false : $this->_normalize($temp);
2317
+ }
2318
+
2319
+ static $zero, $one;
2320
+ if (!isset($zero)) {
2321
+ $zero = new Math_BigInteger();
2322
+ $one = new Math_BigInteger(1);
2323
+ }
2324
+
2325
+ // $x mod $n == $x mod -$n.
2326
+ $n = $n->abs();
2327
+
2328
+ if ($this->compare($zero) < 0) {
2329
+ $temp = $this->abs();
2330
+ $temp = $temp->modInverse($n);
2331
+ return $negated === false ? false : $this->_normalize($n->subtract($temp));
2332
+ }
2333
+
2334
+ extract($this->extendedGCD($n));
2335
+
2336
+ if (!$gcd->equals($one)) {
2337
+ return false;
2338
+ }
2339
+
2340
+ $x = $x->compare($zero) < 0 ? $x->add($n) : $x;
2341
+
2342
+ return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
2343
+ }
2344
+
2345
+ /**
2346
+ * Calculates the greatest common divisor and B�zout's identity.
2347
+ *
2348
+ * Say you have 693 and 609. The GCD is 21. B�zout's identity states that there exist integers x and y such that
2349
+ * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
2350
+ * combination is returned is dependant upon which mode is in use. See
2351
+ * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity B�zout's identity - Wikipedia} for more information.
2352
+ *
2353
+ * Here's an example:
2354
+ * <code>
2355
+ * <?php
2356
+ * include('Math/BigInteger.php');
2357
+ *
2358
+ * $a = new Math_BigInteger(693);
2359
+ * $b = new Math_BigInteger(609);
2360
+ *
2361
+ * extract($a->extendedGCD($b));
2362
+ *
2363
+ * echo $gcd->toString() . "\r\n"; // outputs 21
2364
+ * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
2365
+ * ?>
2366
+ * </code>
2367
+ *
2368
+ * @param Math_BigInteger $n
2369
+ * @return Math_BigInteger
2370
+ * @access public
2371
+ * @internal Calculates the GCD using the binary xGCD algorithim described in
2372
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
2373
+ * the more traditional algorithim requires "relatively costly multiple-precision divisions".
2374
+ */
2375
+ function extendedGCD($n)
2376
+ {
2377
+ switch ( MATH_BIGINTEGER_MODE ) {
2378
+ case MATH_BIGINTEGER_MODE_GMP:
2379
+ extract(gmp_gcdext($this->value, $n->value));
2380
+
2381
+ return array(
2382
+ 'gcd' => $this->_normalize(new Math_BigInteger($g)),
2383
+ 'x' => $this->_normalize(new Math_BigInteger($s)),
2384
+ 'y' => $this->_normalize(new Math_BigInteger($t))
2385
+ );
2386
+ case MATH_BIGINTEGER_MODE_BCMATH:
2387
+ // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
2388
+ // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
2389
+ // the basic extended euclidean algorithim is what we're using.
2390
+
2391
+ $u = $this->value;
2392
+ $v = $n->value;
2393
+
2394
+ $a = '1';
2395
+ $b = '0';
2396
+ $c = '0';
2397
+ $d = '1';
2398
+
2399
+ while (bccomp($v, '0', 0) != 0) {
2400
+ $q = bcdiv($u, $v, 0);
2401
+
2402
+ $temp = $u;
2403
+ $u = $v;
2404
+ $v = bcsub($temp, bcmul($v, $q, 0), 0);
2405
+
2406
+ $temp = $a;
2407
+ $a = $c;
2408
+ $c = bcsub($temp, bcmul($a, $q, 0), 0);
2409
+
2410
+ $temp = $b;
2411
+ $b = $d;
2412
+ $d = bcsub($temp, bcmul($b, $q, 0), 0);
2413
+ }
2414
+
2415
+ return array(
2416
+ 'gcd' => $this->_normalize(new Math_BigInteger($u)),
2417
+ 'x' => $this->_normalize(new Math_BigInteger($a)),
2418
+ 'y' => $this->_normalize(new Math_BigInteger($b))
2419
+ );
2420
+ }
2421
+
2422
+ $y = $n->copy();
2423
+ $x = $this->copy();
2424
+ $g = new Math_BigInteger();
2425
+ $g->value = array(1);
2426
+
2427
+ while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) {
2428
+ $x->_rshift(1);
2429
+ $y->_rshift(1);
2430
+ $g->_lshift(1);
2431
+ }
2432
+
2433
+ $u = $x->copy();
2434
+ $v = $y->copy();
2435
+
2436
+ $a = new Math_BigInteger();
2437
+ $b = new Math_BigInteger();
2438
+ $c = new Math_BigInteger();
2439
+ $d = new Math_BigInteger();
2440
+
2441
+ $a->value = $d->value = $g->value = array(1);
2442
+ $b->value = $c->value = array();
2443
+
2444
+ while ( !empty($u->value) ) {
2445
+ while ( !($u->value[0] & 1) ) {
2446
+ $u->_rshift(1);
2447
+ if ( (!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1)) ) {
2448
+ $a = $a->add($y);
2449
+ $b = $b->subtract($x);
2450
+ }
2451
+ $a->_rshift(1);
2452
+ $b->_rshift(1);
2453
+ }
2454
+
2455
+ while ( !($v->value[0] & 1) ) {
2456
+ $v->_rshift(1);
2457
+ if ( (!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1)) ) {
2458
+ $c = $c->add($y);
2459
+ $d = $d->subtract($x);
2460
+ }
2461
+ $c->_rshift(1);
2462
+ $d->_rshift(1);
2463
+ }
2464
+
2465
+ if ($u->compare($v) >= 0) {
2466
+ $u = $u->subtract($v);
2467
+ $a = $a->subtract($c);
2468
+ $b = $b->subtract($d);
2469
+ } else {
2470
+ $v = $v->subtract($u);
2471
+ $c = $c->subtract($a);
2472
+ $d = $d->subtract($b);
2473
+ }
2474
+ }
2475
+
2476
+ return array(
2477
+ 'gcd' => $this->_normalize($g->multiply($v)),
2478
+ 'x' => $this->_normalize($c),
2479
+ 'y' => $this->_normalize($d)
2480
+ );
2481
+ }
2482
+
2483
+ /**
2484
+ * Calculates the greatest common divisor
2485
+ *
2486
+ * Say you have 693 and 609. The GCD is 21.
2487
+ *
2488
+ * Here's an example:
2489
+ * <code>
2490
+ * <?php
2491
+ * include('Math/BigInteger.php');
2492
+ *
2493
+ * $a = new Math_BigInteger(693);
2494
+ * $b = new Math_BigInteger(609);
2495
+ *
2496
+ * $gcd = a->extendedGCD($b);
2497
+ *
2498
+ * echo $gcd->toString() . "\r\n"; // outputs 21
2499
+ * ?>
2500
+ * </code>
2501
+ *
2502
+ * @param Math_BigInteger $n
2503
+ * @return Math_BigInteger
2504
+ * @access public
2505
+ */
2506
+ function gcd($n)
2507
+ {
2508
+ extract($this->extendedGCD($n));
2509
+ return $gcd;
2510
+ }
2511
+
2512
+ /**
2513
+ * Absolute value.
2514
+ *
2515
+ * @return Math_BigInteger
2516
+ * @access public
2517
+ */
2518
+ function abs()
2519
+ {
2520
+ $temp = new Math_BigInteger();
2521
+
2522
+ switch ( MATH_BIGINTEGER_MODE ) {
2523
+ case MATH_BIGINTEGER_MODE_GMP:
2524
+ $temp->value = gmp_abs($this->value);
2525
+ break;
2526
+ case MATH_BIGINTEGER_MODE_BCMATH:
2527
+ $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
2528
+ break;
2529
+ default:
2530
+ $temp->value = $this->value;
2531
+ }
2532
+
2533
+ return $temp;
2534
+ }
2535
+
2536
+ /**
2537
+ * Compares two numbers.
2538
+ *
2539
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
2540
+ * demonstrated thusly:
2541
+ *
2542
+ * $x > $y: $x->compare($y) > 0
2543
+ * $x < $y: $x->compare($y) < 0
2544
+ * $x == $y: $x->compare($y) == 0
2545
+ *
2546
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
2547
+ *
2548
+ * @param Math_BigInteger $x
2549
+ * @return Integer < 0 if $this is less than $x; > 0 if $this is greater than $x, and 0 if they are equal.
2550
+ * @access public
2551
+ * @see equals()
2552
+ * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
2553
+ */
2554
+ function compare($y)
2555
+ {
2556
+ switch ( MATH_BIGINTEGER_MODE ) {
2557
+ case MATH_BIGINTEGER_MODE_GMP:
2558
+ return gmp_cmp($this->value, $y->value);
2559
+ case MATH_BIGINTEGER_MODE_BCMATH:
2560
+ return bccomp($this->value, $y->value, 0);
2561
+ }
2562
+
2563
+ return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
2564
+ }
2565
+
2566
+ /**
2567
+ * Compares two numbers.
2568
+ *
2569
+ * @param Array $x_value
2570
+ * @param Boolean $x_negative
2571
+ * @param Array $y_value
2572
+ * @param Boolean $y_negative
2573
+ * @return Integer
2574
+ * @see compare()
2575
+ * @access private
2576
+ */
2577
+ function _compare($x_value, $x_negative, $y_value, $y_negative)
2578
+ {
2579
+ if ( $x_negative != $y_negative ) {
2580
+ return ( !$x_negative && $y_negative ) ? 1 : -1;
2581
+ }
2582
+
2583
+ $result = $x_negative ? -1 : 1;
2584
+
2585
+ if ( count($x_value) != count($y_value) ) {
2586
+ return ( count($x_value) > count($y_value) ) ? $result : -$result;
2587
+ }
2588
+ $size = max(count($x_value), count($y_value));
2589
+
2590
+ $x_value = array_pad($x_value, $size, 0);
2591
+ $y_value = array_pad($y_value, $size, 0);
2592
+
2593
+ for ($i = count($x_value) - 1; $i >= 0; --$i) {
2594
+ if ($x_value[$i] != $y_value[$i]) {
2595
+ return ( $x_value[$i] > $y_value[$i] ) ? $result : -$result;
2596
+ }
2597
+ }
2598
+
2599
+ return 0;
2600
+ }
2601
+
2602
+ /**
2603
+ * Tests the equality of two numbers.
2604
+ *
2605
+ * If you need to see if one number is greater than or less than another number, use Math_BigInteger::compare()
2606
+ *
2607
+ * @param Math_BigInteger $x
2608
+ * @return Boolean
2609
+ * @access public
2610
+ * @see compare()
2611
+ */
2612
+ function equals($x)
2613
+ {
2614
+ switch ( MATH_BIGINTEGER_MODE ) {
2615
+ case MATH_BIGINTEGER_MODE_GMP:
2616
+ return gmp_cmp($this->value, $x->value) == 0;
2617
+ default:
2618
+ return $this->value === $x->value && $this->is_negative == $x->is_negative;
2619
+ }
2620
+ }
2621
+
2622
+ /**
2623
+ * Set Precision
2624
+ *
2625
+ * Some bitwise operations give different results depending on the precision being used. Examples include left
2626
+ * shift, not, and rotates.
2627
+ *
2628
+ * @param Math_BigInteger $x
2629
+ * @access public
2630
+ * @return Math_BigInteger
2631
+ */
2632
+ function setPrecision($bits)
2633
+ {
2634
+ $this->precision = $bits;
2635
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) {
2636
+ $this->bitmask = new Math_BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
2637
+ } else {
2638
+ $this->bitmask = new Math_BigInteger(bcpow('2', $bits, 0));
2639
+ }
2640
+
2641
+ $temp = $this->_normalize($this);
2642
+ $this->value = $temp->value;
2643
+ }
2644
+
2645
+ /**
2646
+ * Logical And
2647
+ *
2648
+ * @param Math_BigInteger $x
2649
+ * @access public
2650
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
2651
+ * @return Math_BigInteger
2652
+ */
2653
+ function bitwise_and($x)
2654
+ {
2655
+ switch ( MATH_BIGINTEGER_MODE ) {
2656
+ case MATH_BIGINTEGER_MODE_GMP:
2657
+ $temp = new Math_BigInteger();
2658
+ $temp->value = gmp_and($this->value, $x->value);
2659
+
2660
+ return $this->_normalize($temp);
2661
+ case MATH_BIGINTEGER_MODE_BCMATH:
2662
+ $left = $this->toBytes();
2663
+ $right = $x->toBytes();
2664
+
2665
+ $length = max(strlen($left), strlen($right));
2666
+
2667
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
2668
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
2669
+
2670
+ return $this->_normalize(new Math_BigInteger($left & $right, 256));
2671
+ }
2672
+
2673
+ $result = $this->copy();
2674
+
2675
+ $length = min(count($x->value), count($this->value));
2676
+
2677
+ $result->value = array_slice($result->value, 0, $length);
2678
+
2679
+ for ($i = 0; $i < $length; ++$i) {
2680
+ $result->value[$i] = $result->value[$i] & $x->value[$i];
2681
+ }
2682
+
2683
+ return $this->_normalize($result);
2684
+ }
2685
+
2686
+ /**
2687
+ * Logical Or
2688
+ *
2689
+ * @param Math_BigInteger $x
2690
+ * @access public
2691
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
2692
+ * @return Math_BigInteger
2693
+ */
2694
+ function bitwise_or($x)
2695
+ {
2696
+ switch ( MATH_BIGINTEGER_MODE ) {
2697
+ case MATH_BIGINTEGER_MODE_GMP:
2698
+ $temp = new Math_BigInteger();
2699
+ $temp->value = gmp_or($this->value, $x->value);
2700
+
2701
+ return $this->_normalize($temp);
2702
+ case MATH_BIGINTEGER_MODE_BCMATH:
2703
+ $left = $this->toBytes();
2704
+ $right = $x->toBytes();
2705
+
2706
+ $length = max(strlen($left), strlen($right));
2707
+
2708
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
2709
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
2710
+
2711
+ return $this->_normalize(new Math_BigInteger($left | $right, 256));
2712
+ }
2713
+
2714
+ $length = max(count($this->value), count($x->value));
2715
+ $result = $this->copy();
2716
+ $result->value = array_pad($result->value, 0, $length);
2717
+ $x->value = array_pad($x->value, 0, $length);
2718
+
2719
+ for ($i = 0; $i < $length; ++$i) {
2720
+ $result->value[$i] = $this->value[$i] | $x->value[$i];
2721
+ }
2722
+
2723
+ return $this->_normalize($result);
2724
+ }
2725
+
2726
+ /**
2727
+ * Logical Exclusive-Or
2728
+ *
2729
+ * @param Math_BigInteger $x
2730
+ * @access public
2731
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
2732
+ * @return Math_BigInteger
2733
+ */
2734
+ function bitwise_xor($x)
2735
+ {
2736
+ switch ( MATH_BIGINTEGER_MODE ) {
2737
+ case MATH_BIGINTEGER_MODE_GMP:
2738
+ $temp = new Math_BigInteger();
2739
+ $temp->value = gmp_xor($this->value, $x->value);
2740
+
2741
+ return $this->_normalize($temp);
2742
+ case MATH_BIGINTEGER_MODE_BCMATH:
2743
+ $left = $this->toBytes();
2744
+ $right = $x->toBytes();
2745
+
2746
+ $length = max(strlen($left), strlen($right));
2747
+
2748
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
2749
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
2750
+
2751
+ return $this->_normalize(new Math_BigInteger($left ^ $right, 256));
2752
+ }
2753
+
2754
+ $length = max(count($this->value), count($x->value));
2755
+ $result = $this->copy();
2756
+ $result->value = array_pad($result->value, 0, $length);
2757
+ $x->value = array_pad($x->value, 0, $length);
2758
+
2759
+ for ($i = 0; $i < $length; ++$i) {
2760
+ $result->value[$i] = $this->value[$i] ^ $x->value[$i];
2761
+ }
2762
+
2763
+ return $this->_normalize($result);
2764
+ }
2765
+
2766
+ /**
2767
+ * Logical Not
2768
+ *
2769
+ * @access public
2770
+ * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
2771
+ * @return Math_BigInteger
2772
+ */
2773
+ function bitwise_not()
2774
+ {
2775
+ // calculuate "not" without regard to $this->precision
2776
+ // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
2777
+ $temp = $this->toBytes();
2778
+ $pre_msb = decbin(ord($temp[0]));
2779
+ $temp = ~$temp;
2780
+ $msb = decbin(ord($temp[0]));
2781
+ if (strlen($msb) == 8) {
2782
+ $msb = substr($msb, strpos($msb, '0'));
2783
+ }
2784
+ $temp[0] = chr(bindec($msb));
2785
+
2786
+ // see if we need to add extra leading 1's
2787
+ $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
2788
+ $new_bits = $this->precision - $current_bits;
2789
+ if ($new_bits <= 0) {
2790
+ return $this->_normalize(new Math_BigInteger($temp, 256));
2791
+ }
2792
+
2793
+ // generate as many leading 1's as we need to.
2794
+ $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
2795
+ $this->_base256_lshift($leading_ones, $current_bits);
2796
+
2797
+ $temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT);
2798
+
2799
+ return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256));
2800
+ }
2801
+
2802
+ /**
2803
+ * Logical Right Shift
2804
+ *
2805
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
2806
+ *
2807
+ * @param Integer $shift
2808
+ * @return Math_BigInteger
2809
+ * @access public
2810
+ * @internal The only version that yields any speed increases is the internal version.
2811
+ */
2812
+ function bitwise_rightShift($shift)
2813
+ {
2814
+ $temp = new Math_BigInteger();
2815
+
2816
+ switch ( MATH_BIGINTEGER_MODE ) {
2817
+ case MATH_BIGINTEGER_MODE_GMP:
2818
+ static $two;
2819
+
2820
+ if (!isset($two)) {
2821
+ $two = gmp_init('2');
2822
+ }
2823
+
2824
+ $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
2825
+
2826
+ break;
2827
+ case MATH_BIGINTEGER_MODE_BCMATH:
2828
+ $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
2829
+
2830
+ break;
2831
+ default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
2832
+ // and I don't want to do that...
2833
+ $temp->value = $this->value;
2834
+ $temp->_rshift($shift);
2835
+ }
2836
+
2837
+ return $this->_normalize($temp);
2838
+ }
2839
+
2840
+ /**
2841
+ * Logical Left Shift
2842
+ *
2843
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
2844
+ *
2845
+ * @param Integer $shift
2846
+ * @return Math_BigInteger
2847
+ * @access public
2848
+ * @internal The only version that yields any speed increases is the internal version.
2849
+ */
2850
+ function bitwise_leftShift($shift)
2851
+ {
2852
+ $temp = new Math_BigInteger();
2853
+
2854
+ switch ( MATH_BIGINTEGER_MODE ) {
2855
+ case MATH_BIGINTEGER_MODE_GMP:
2856
+ static $two;
2857
+
2858
+ if (!isset($two)) {
2859
+ $two = gmp_init('2');
2860
+ }
2861
+
2862
+ $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
2863
+
2864
+ break;
2865
+ case MATH_BIGINTEGER_MODE_BCMATH:
2866
+ $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
2867
+
2868
+ break;
2869
+ default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
2870
+ // and I don't want to do that...
2871
+ $temp->value = $this->value;
2872
+ $temp->_lshift($shift);
2873
+ }
2874
+
2875
+ return $this->_normalize($temp);
2876
+ }
2877
+
2878
+ /**
2879
+ * Logical Left Rotate
2880
+ *
2881
+ * Instead of the top x bits being dropped they're appended to the shifted bit string.
2882
+ *
2883
+ * @param Integer $shift
2884
+ * @return Math_BigInteger
2885
+ * @access public
2886
+ */
2887
+ function bitwise_leftRotate($shift)
2888
+ {
2889
+ $bits = $this->toBytes();
2890
+
2891
+ if ($this->precision > 0) {
2892
+ $precision = $this->precision;
2893
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
2894
+ $mask = $this->bitmask->subtract(new Math_BigInteger(1));
2895
+ $mask = $mask->toBytes();
2896
+ } else {
2897
+ $mask = $this->bitmask->toBytes();
2898
+ }
2899
+ } else {
2900
+ $temp = ord($bits[0]);
2901
+ for ($i = 0; $temp >> $i; ++$i);
2902
+ $precision = 8 * strlen($bits) - 8 + $i;
2903
+ $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
2904
+ }
2905
+
2906
+ if ($shift < 0) {
2907
+ $shift+= $precision;
2908
+ }
2909
+ $shift%= $precision;
2910
+
2911
+ if (!$shift) {
2912
+ return $this->copy();
2913
+ }
2914
+
2915
+ $left = $this->bitwise_leftShift($shift);
2916
+ $left = $left->bitwise_and(new Math_BigInteger($mask, 256));
2917
+ $right = $this->bitwise_rightShift($precision - $shift);
2918
+ $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
2919
+ return $this->_normalize($result);
2920
+ }
2921
+
2922
+ /**
2923
+ * Logical Right Rotate
2924
+ *
2925
+ * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
2926
+ *
2927
+ * @param Integer $shift
2928
+ * @return Math_BigInteger
2929
+ * @access public
2930
+ */
2931
+ function bitwise_rightRotate($shift)
2932
+ {
2933
+ return $this->bitwise_leftRotate(-$shift);
2934
+ }
2935
+
2936
+ /**
2937
+ * Set random number generator function
2938
+ *
2939
+ * $generator should be the name of a random generating function whose first parameter is the minimum
2940
+ * value and whose second parameter is the maximum value. If this function needs to be seeded, it should
2941
+ * be seeded prior to calling Math_BigInteger::random() or Math_BigInteger::randomPrime()
2942
+ *
2943
+ * If the random generating function is not explicitly set, it'll be assumed to be mt_rand().
2944
+ *
2945
+ * @see random()
2946
+ * @see randomPrime()
2947
+ * @param optional String $generator
2948
+ * @access public
2949
+ */
2950
+ function setRandomGenerator($generator)
2951
+ {
2952
+ $this->generator = $generator;
2953
+ }
2954
+
2955
+ /**
2956
+ * Generate a random number
2957
+ *
2958
+ * @param optional Integer $min
2959
+ * @param optional Integer $max
2960
+ * @return Math_BigInteger
2961
+ * @access public
2962
+ */
2963
+ function random($min = false, $max = false)
2964
+ {
2965
+ if ($min === false) {
2966
+ $min = new Math_BigInteger(0);
2967
+ }
2968
+
2969
+ if ($max === false) {
2970
+ $max = new Math_BigInteger(0x7FFFFFFF);
2971
+ }
2972
+
2973
+ $compare = $max->compare($min);
2974
+
2975
+ if (!$compare) {
2976
+ return $this->_normalize($min);
2977
+ } else if ($compare < 0) {
2978
+ // if $min is bigger then $max, swap $min and $max
2979
+ $temp = $max;
2980
+ $max = $min;
2981
+ $min = $temp;
2982
+ }
2983
+
2984
+ $generator = $this->generator;
2985
+
2986
+ $max = $max->subtract($min);
2987
+ $max = ltrim($max->toBytes(), chr(0));
2988
+ $size = strlen($max) - 1;
2989
+ $random = '';
2990
+
2991
+ $bytes = $size & 1;
2992
+ for ($i = 0; $i < $bytes; ++$i) {
2993
+ $random.= chr($generator(0, 255));
2994
+ }
2995
+
2996
+ $blocks = $size >> 1;
2997
+ for ($i = 0; $i < $blocks; ++$i) {
2998
+ // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
2999
+ $random.= pack('n', $generator(0, 0xFFFF));
3000
+ }
3001
+
3002
+ $temp = new Math_BigInteger($random, 256);
3003
+ if ($temp->compare(new Math_BigInteger(substr($max, 1), 256)) > 0) {
3004
+ $random = chr($generator(0, ord($max[0]) - 1)) . $random;
3005
+ } else {
3006
+ $random = chr($generator(0, ord($max[0]) )) . $random;
3007
+ }
3008
+
3009
+ $random = new Math_BigInteger($random, 256);
3010
+
3011
+ return $this->_normalize($random->add($min));
3012
+ }
3013
+
3014
+ /**
3015
+ * Generate a random prime number.
3016
+ *
3017
+ * If there's not a prime within the given range, false will be returned. If more than $timeout seconds have elapsed,
3018
+ * give up and return false.
3019
+ *
3020
+ * @param optional Integer $min
3021
+ * @param optional Integer $max
3022
+ * @param optional Integer $timeout
3023
+ * @return Math_BigInteger
3024
+ * @access public
3025
+ * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
3026
+ */
3027
+ function randomPrime($min = false, $max = false, $timeout = false)
3028
+ {
3029
+ $compare = $max->compare($min);
3030
+
3031
+ if (!$compare) {
3032
+ return $min;
3033
+ } else if ($compare < 0) {
3034
+ // if $min is bigger then $max, swap $min and $max
3035
+ $temp = $max;
3036
+ $max = $min;
3037
+ $min = $temp;
3038
+ }
3039
+
3040
+ // gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
3041
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) {
3042
+ // we don't rely on Math_BigInteger::random()'s min / max when gmp_nextprime() is being used since this function
3043
+ // does its own checks on $max / $min when gmp_nextprime() is used. When gmp_nextprime() is not used, however,
3044
+ // the same $max / $min checks are not performed.
3045
+ if ($min === false) {
3046
+ $min = new Math_BigInteger(0);
3047
+ }
3048
+
3049
+ if ($max === false) {
3050
+ $max = new Math_BigInteger(0x7FFFFFFF);
3051
+ }
3052
+
3053
+ $x = $this->random($min, $max);
3054
+
3055
+ $x->value = gmp_nextprime($x->value);
3056
+
3057
+ if ($x->compare($max) <= 0) {
3058
+ return $x;
3059
+ }
3060
+
3061
+ $x->value = gmp_nextprime($min->value);
3062
+
3063
+ if ($x->compare($max) <= 0) {
3064
+ return $x;
3065
+ }
3066
+
3067
+ return false;
3068
+ }
3069
+
3070
+ static $one, $two;
3071
+ if (!isset($one)) {
3072
+ $one = new Math_BigInteger(1);
3073
+ $two = new Math_BigInteger(2);
3074
+ }
3075
+
3076
+ $start = time();
3077
+
3078
+ $x = $this->random($min, $max);
3079
+ if ($x->equals($two)) {
3080
+ return $x;
3081
+ }
3082
+
3083
+ $x->_make_odd();
3084
+ if ($x->compare($max) > 0) {
3085
+ // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
3086
+ if ($min->equals($max)) {
3087
+ return false;
3088
+ }
3089
+ $x = $min->copy();
3090
+ $x->_make_odd();
3091
+ }
3092
+
3093
+ $initial_x = $x->copy();
3094
+
3095
+ while (true) {
3096
+ if ($timeout !== false && time() - $start > $timeout) {
3097
+ return false;
3098
+ }
3099
+
3100
+ if ($x->isPrime()) {
3101
+ return $x;
3102
+ }
3103
+
3104
+ $x = $x->add($two);
3105
+
3106
+ if ($x->compare($max) > 0) {
3107
+ $x = $min->copy();
3108
+ if ($x->equals($two)) {
3109
+ return $x;
3110
+ }
3111
+ $x->_make_odd();
3112
+ }
3113
+
3114
+ if ($x->equals($initial_x)) {
3115
+ return false;
3116
+ }
3117
+ }
3118
+ }
3119
+
3120
+ /**
3121
+ * Make the current number odd
3122
+ *
3123
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
3124
+ *
3125
+ * @see randomPrime()
3126
+ * @access private
3127
+ */
3128
+ function _make_odd()
3129
+ {
3130
+ switch ( MATH_BIGINTEGER_MODE ) {
3131
+ case MATH_BIGINTEGER_MODE_GMP:
3132
+ gmp_setbit($this->value, 0);
3133
+ break;
3134
+ case MATH_BIGINTEGER_MODE_BCMATH:
3135
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
3136
+ $this->value = bcadd($this->value, '1');
3137
+ }
3138
+ break;
3139
+ default:
3140
+ $this->value[0] |= 1;
3141
+ }
3142
+ }
3143
+
3144
+ /**
3145
+ * Checks a numer to see if it's prime
3146
+ *
3147
+ * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
3148
+ * $t parameter is distributability. Math_BigInteger::randomPrime() can be distributed accross multiple pageloads
3149
+ * on a website instead of just one.
3150
+ *
3151
+ * @param optional Integer $t
3152
+ * @return Boolean
3153
+ * @access public
3154
+ * @internal Uses the
3155
+ * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See
3156
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
3157
+ */
3158
+ function isPrime($t = false)
3159
+ {
3160
+ $length = strlen($this->toBytes());
3161
+
3162
+ if (!$t) {
3163
+ // see HAC 4.49 "Note (controlling the error probability)"
3164
+ if ($length >= 163) { $t = 2; } // floor(1300 / 8)
3165
+ else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
3166
+ else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
3167
+ else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
3168
+ else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
3169
+ else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
3170
+ else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
3171
+ else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
3172
+ else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
3173
+ else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
3174
+ else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
3175
+ else { $t = 27; }
3176
+ }
3177
+
3178
+ // ie. gmp_testbit($this, 0)
3179
+ // ie. isEven() or !isOdd()
3180
+ switch ( MATH_BIGINTEGER_MODE ) {
3181
+ case MATH_BIGINTEGER_MODE_GMP:
3182
+ return gmp_prob_prime($this->value, $t) != 0;
3183
+ case MATH_BIGINTEGER_MODE_BCMATH:
3184
+ if ($this->value === '2') {
3185
+ return true;
3186
+ }
3187
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
3188
+ return false;
3189
+ }
3190
+ break;
3191
+ default:
3192
+ if ($this->value == array(2)) {
3193
+ return true;
3194
+ }
3195
+ if (~$this->value[0] & 1) {
3196
+ return false;
3197
+ }
3198
+ }
3199
+
3200
+ static $primes, $zero, $one, $two;
3201
+
3202
+ if (!isset($primes)) {
3203
+ $primes = array(
3204
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
3205
+ 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
3206
+ 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
3207
+ 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
3208
+ 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
3209
+ 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
3210
+ 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
3211
+ 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
3212
+ 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
3213
+ 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
3214
+ 953, 967, 971, 977, 983, 991, 997
3215
+ );
3216
+
3217
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) {
3218
+ for ($i = 0; $i < count($primes); ++$i) {
3219
+ $primes[$i] = new Math_BigInteger($primes[$i]);
3220
+ }
3221
+ }
3222
+
3223
+ $zero = new Math_BigInteger();
3224
+ $one = new Math_BigInteger(1);
3225
+ $two = new Math_BigInteger(2);
3226
+ }
3227
+
3228
+ if ($this->equals($one)) {
3229
+ return false;
3230
+ }
3231
+
3232
+ // see HAC 4.4.1 "Random search for probable primes"
3233
+ if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) {
3234
+ foreach ($primes as $prime) {
3235
+ list(, $r) = $this->divide($prime);
3236
+ if ($r->equals($zero)) {
3237
+ return $this->equals($prime);
3238
+ }
3239
+ }
3240
+ } else {
3241
+ $value = $this->value;
3242
+ foreach ($primes as $prime) {
3243
+ list(, $r) = $this->_divide_digit($value, $prime);
3244
+ if (!$r) {
3245
+ return count($value) == 1 && $value[0] == $prime;
3246
+ }
3247
+ }
3248
+ }
3249
+
3250
+ $n = $this->copy();
3251
+ $n_1 = $n->subtract($one);
3252
+ $n_2 = $n->subtract($two);
3253
+
3254
+ $r = $n_1->copy();
3255
+ $r_value = $r->value;
3256
+ // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
3257
+ if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
3258
+ $s = 0;
3259
+ // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
3260
+ while ($r->value[strlen($r->value) - 1] % 2 == 0) {
3261
+ $r->value = bcdiv($r->value, '2', 0);
3262
+ ++$s;
3263
+ }
3264
+ } else {
3265
+ for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
3266
+ $temp = ~$r_value[$i] & 0xFFFFFF;
3267
+ for ($j = 1; ($temp >> $j) & 1; ++$j);
3268
+ if ($j != 25) {
3269
+ break;
3270
+ }
3271
+ }
3272
+ $s = 26 * $i + $j - 1;
3273
+ $r->_rshift($s);
3274
+ }
3275
+
3276
+ for ($i = 0; $i < $t; ++$i) {
3277
+ $a = $this->random($two, $n_2);
3278
+ $y = $a->modPow($r, $n);
3279
+
3280
+ if (!$y->equals($one) && !$y->equals($n_1)) {
3281
+ for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
3282
+ $y = $y->modPow($two, $n);
3283
+ if ($y->equals($one)) {
3284
+ return false;
3285
+ }
3286
+ }
3287
+
3288
+ if (!$y->equals($n_1)) {
3289
+ return false;
3290
+ }
3291
+ }
3292
+ }
3293
+ return true;
3294
+ }
3295
+
3296
+ /**
3297
+ * Logical Left Shift
3298
+ *
3299
+ * Shifts BigInteger's by $shift bits.
3300
+ *
3301
+ * @param Integer $shift
3302
+ * @access private
3303
+ */
3304
+ function _lshift($shift)
3305
+ {
3306
+ if ( $shift == 0 ) {
3307
+ return;
3308
+ }
3309
+
3310
+ $num_digits = (int) ($shift / 26);
3311
+ $shift %= 26;
3312
+ $shift = 1 << $shift;
3313
+
3314
+ $carry = 0;
3315
+
3316
+ for ($i = 0; $i < count($this->value); ++$i) {
3317
+ $temp = $this->value[$i] * $shift + $carry;
3318
+ $carry = (int) ($temp / 0x4000000);
3319
+ $this->value[$i] = (int) ($temp - $carry * 0x4000000);
3320
+ }
3321
+
3322
+ if ( $carry ) {
3323
+ $this->value[] = $carry;
3324
+ }
3325
+
3326
+ while ($num_digits--) {
3327
+ array_unshift($this->value, 0);
3328
+ }
3329
+ }
3330
+
3331
+ /**
3332
+ * Logical Right Shift
3333
+ *
3334
+ * Shifts BigInteger's by $shift bits.
3335
+ *
3336
+ * @param Integer $shift
3337
+ * @access private
3338
+ */
3339
+ function _rshift($shift)
3340
+ {
3341
+ if ($shift == 0) {
3342
+ return;
3343
+ }
3344
+
3345
+ $num_digits = (int) ($shift / 26);
3346
+ $shift %= 26;
3347
+ $carry_shift = 26 - $shift;
3348
+ $carry_mask = (1 << $shift) - 1;
3349
+
3350
+ if ( $num_digits ) {
3351
+ $this->value = array_slice($this->value, $num_digits);
3352
+ }
3353
+
3354
+ $carry = 0;
3355
+
3356
+ for ($i = count($this->value) - 1; $i >= 0; --$i) {
3357
+ $temp = $this->value[$i] >> $shift | $carry;
3358
+ $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
3359
+ $this->value[$i] = $temp;
3360
+ }
3361
+
3362
+ $this->value = $this->_trim($this->value);
3363
+ }
3364
+
3365
+ /**
3366
+ * Normalize
3367
+ *
3368
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
3369
+ *
3370
+ * @param Math_BigInteger
3371
+ * @return Math_BigInteger
3372
+ * @see _trim()
3373
+ * @access private
3374
+ */
3375
+ function _normalize($result)
3376
+ {
3377
+ $result->precision = $this->precision;
3378
+ $result->bitmask = $this->bitmask;
3379
+
3380
+ switch ( MATH_BIGINTEGER_MODE ) {
3381
+ case MATH_BIGINTEGER_MODE_GMP:
3382
+ if (!empty($result->bitmask->value)) {
3383
+ $result->value = gmp_and($result->value, $result->bitmask->value);
3384
+ }
3385
+
3386
+ return $result;
3387
+ case MATH_BIGINTEGER_MODE_BCMATH:
3388
+ if (!empty($result->bitmask->value)) {
3389
+ $result->value = bcmod($result->value, $result->bitmask->value);
3390
+ }
3391
+
3392
+ return $result;
3393
+ }
3394
+
3395
+ $value = &$result->value;
3396
+
3397
+ if ( !count($value) ) {
3398
+ return $result;
3399
+ }
3400
+
3401
+ $value = $this->_trim($value);
3402
+
3403
+ if (!empty($result->bitmask->value)) {
3404
+ $length = min(count($value), count($this->bitmask->value));
3405
+ $value = array_slice($value, 0, $length);
3406
+
3407
+ for ($i = 0; $i < $length; ++$i) {
3408
+ $value[$i] = $value[$i] & $this->bitmask->value[$i];
3409
+ }
3410
+ }
3411
+
3412
+ return $result;
3413
+ }
3414
+
3415
+ /**
3416
+ * Trim
3417
+ *
3418
+ * Removes leading zeros
3419
+ *
3420
+ * @return Math_BigInteger
3421
+ * @access private
3422
+ */
3423
+ function _trim($value)
3424
+ {
3425
+ for ($i = count($value) - 1; $i >= 0; --$i) {
3426
+ if ( $value[$i] ) {
3427
+ break;
3428
+ }
3429
+ unset($value[$i]);
3430
+ }
3431
+
3432
+ return $value;
3433
+ }
3434
+
3435
+ /**
3436
+ * Array Repeat
3437
+ *
3438
+ * @param $input Array
3439
+ * @param $multiplier mixed
3440
+ * @return Array
3441
+ * @access private
3442
+ */
3443
+ function _array_repeat($input, $multiplier)
3444
+ {
3445
+ return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
3446
+ }
3447
+
3448
+ /**
3449
+ * Logical Left Shift
3450
+ *
3451
+ * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
3452
+ *
3453
+ * @param $x String
3454
+ * @param $shift Integer
3455
+ * @return String
3456
+ * @access private
3457
+ */
3458
+ function _base256_lshift(&$x, $shift)
3459
+ {
3460
+ if ($shift == 0) {
3461
+ return;
3462
+ }
3463
+
3464
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
3465
+ $shift &= 7; // eg. $shift % 8
3466
+
3467
+ $carry = 0;
3468
+ for ($i = strlen($x) - 1; $i >= 0; --$i) {
3469
+ $temp = ord($x[$i]) << $shift | $carry;
3470
+ $x[$i] = chr($temp);
3471
+ $carry = $temp >> 8;
3472
+ }
3473
+ $carry = ($carry != 0) ? chr($carry) : '';
3474
+ $x = $carry . $x . str_repeat(chr(0), $num_bytes);
3475
+ }
3476
+
3477
+ /**
3478
+ * Logical Right Shift
3479
+ *
3480
+ * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
3481
+ *
3482
+ * @param $x String
3483
+ * @param $shift Integer
3484
+ * @return String
3485
+ * @access private
3486
+ */
3487
+ function _base256_rshift(&$x, $shift)
3488
+ {
3489
+ if ($shift == 0) {
3490
+ $x = ltrim($x, chr(0));
3491
+ return '';
3492
+ }
3493
+
3494
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
3495
+ $shift &= 7; // eg. $shift % 8
3496
+
3497
+ $remainder = '';
3498
+ if ($num_bytes) {
3499
+ $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
3500
+ $remainder = substr($x, $start);
3501
+ $x = substr($x, 0, -$num_bytes);
3502
+ }
3503
+
3504
+ $carry = 0;
3505
+ $carry_shift = 8 - $shift;
3506
+ for ($i = 0; $i < strlen($x); ++$i) {
3507
+ $temp = (ord($x[$i]) >> $shift) | $carry;
3508
+ $carry = (ord($x[$i]) << $carry_shift) & 0xFF;
3509
+ $x[$i] = chr($temp);
3510
+ }
3511
+ $x = ltrim($x, chr(0));
3512
+
3513
+ $remainder = chr($carry >> $carry_shift) . $remainder;
3514
+
3515
+ return ltrim($remainder, chr(0));
3516
+ }
3517
+
3518
+ // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
3519
+ // at 32-bits, while java's longs are 64-bits.
3520
+
3521
+ /**
3522
+ * Converts 32-bit integers to bytes.
3523
+ *
3524
+ * @param Integer $x
3525
+ * @return String
3526
+ * @access private
3527
+ */
3528
+ function _int2bytes($x)
3529
+ {
3530
+ return ltrim(pack('N', $x), chr(0));
3531
+ }
3532
+
3533
+ /**
3534
+ * Converts bytes to 32-bit integers
3535
+ *
3536
+ * @param String $x
3537
+ * @return Integer
3538
+ * @access private
3539
+ */
3540
+ function _bytes2int($x)
3541
+ {
3542
+ $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
3543
+ return $temp['int'];
3544
+ }
3545
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/Net/SFTP.php ADDED
@@ -0,0 +1,1461 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of SFTP.
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Currently only supports SFTPv3, which, according to wikipedia.org, "is the most widely used version,
10
+ * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
11
+ * to an SFTPv4/5/6 server.
12
+ *
13
+ * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
14
+ *
15
+ * Here's a short example of how to use this library:
16
+ * <code>
17
+ * <?php
18
+ * include('Net/SFTP.php');
19
+ *
20
+ * $sftp = new Net_SFTP('www.domain.tld');
21
+ * if (!$sftp->login('username', 'password')) {
22
+ * exit('Login Failed');
23
+ * }
24
+ *
25
+ * echo $sftp->pwd() . "\r\n";
26
+ * $sftp->put('filename.ext', 'hello, world!');
27
+ * print_r($sftp->nlist());
28
+ * ?>
29
+ * </code>
30
+ *
31
+ * LICENSE: This library is free software; you can redistribute it and/or
32
+ * modify it under the terms of the GNU Lesser General Public
33
+ * License as published by the Free Software Foundation; either
34
+ * version 2.1 of the License, or (at your option) any later version.
35
+ *
36
+ * This library is distributed in the hope that it will be useful,
37
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
38
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39
+ * Lesser General Public License for more details.
40
+ *
41
+ * You should have received a copy of the GNU Lesser General Public
42
+ * License along with this library; if not, write to the Free Software
43
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
44
+ * MA 02111-1307 USA
45
+ *
46
+ * @category Net
47
+ * @package Net_SFTP
48
+ * @author Jim Wigginton <terrafrost@php.net>
49
+ * @copyright MMIX Jim Wigginton
50
+ * @license http://www.gnu.org/licenses/lgpl.txt
51
+ * @version $Id: SFTP.php,v 1.21 2010/04/09 02:31:34 terrafrost Exp $
52
+ * @link http://phpseclib.sourceforge.net
53
+ */
54
+
55
+ /**
56
+ * Include Net_SSH2
57
+ */
58
+ require_once('phpseclib/Net/SSH2.php');
59
+
60
+ /**#@+
61
+ * @access public
62
+ * @see Net_SFTP::getLog()
63
+ */
64
+ /**
65
+ * Returns the message numbers
66
+ */
67
+ define('NET_SFTP_LOG_SIMPLE', NET_SSH2_LOG_SIMPLE);
68
+ /**
69
+ * Returns the message content
70
+ */
71
+ define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX);
72
+ /**#@-*/
73
+
74
+ /**
75
+ * SFTP channel constant
76
+ *
77
+ * Net_SSH2::exec() uses 0 and Net_SSH2::interactiveRead() / Net_SSH2::interactiveWrite() use 1.
78
+ *
79
+ * @see Net_SSH2::_send_channel_packet()
80
+ * @see Net_SSH2::_get_channel_packet()
81
+ * @access private
82
+ */
83
+ define('NET_SFTP_CHANNEL', 2);
84
+
85
+ /**#@+
86
+ * @access public
87
+ * @see Net_SFTP::put()
88
+ */
89
+ /**
90
+ * Reads data from a local file.
91
+ */
92
+ define('NET_SFTP_LOCAL_FILE', 1);
93
+ /**
94
+ * Reads data from a string.
95
+ */
96
+ define('NET_SFTP_STRING', 2);
97
+ /**#@-*/
98
+
99
+ /**
100
+ * Pure-PHP implementations of SFTP.
101
+ *
102
+ * @author Jim Wigginton <terrafrost@php.net>
103
+ * @version 0.1.0
104
+ * @access public
105
+ * @package Net_SFTP
106
+ */
107
+ class Net_SFTP extends Net_SSH2 {
108
+ /**
109
+ * Packet Types
110
+ *
111
+ * @see Net_SFTP::Net_SFTP()
112
+ * @var Array
113
+ * @access private
114
+ */
115
+ var $packet_types = array();
116
+
117
+ /**
118
+ * Status Codes
119
+ *
120
+ * @see Net_SFTP::Net_SFTP()
121
+ * @var Array
122
+ * @access private
123
+ */
124
+ var $status_codes = array();
125
+
126
+ /**
127
+ * The Request ID
128
+ *
129
+ * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
130
+ * concurrent actions, so it's somewhat academic, here.
131
+ *
132
+ * @var Integer
133
+ * @see Net_SFTP::_send_sftp_packet()
134
+ * @access private
135
+ */
136
+ var $request_id = false;
137
+
138
+ /**
139
+ * The Packet Type
140
+ *
141
+ * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
142
+ * concurrent actions, so it's somewhat academic, here.
143
+ *
144
+ * @var Integer
145
+ * @see Net_SFTP::_get_sftp_packet()
146
+ * @access private
147
+ */
148
+ var $packet_type = -1;
149
+
150
+ /**
151
+ * Packet Buffer
152
+ *
153
+ * @var String
154
+ * @see Net_SFTP::_get_sftp_packet()
155
+ * @access private
156
+ */
157
+ var $packet_buffer = '';
158
+
159
+ /**
160
+ * Extensions supported by the server
161
+ *
162
+ * @var Array
163
+ * @see Net_SFTP::_initChannel()
164
+ * @access private
165
+ */
166
+ var $extensions = array();
167
+
168
+ /**
169
+ * Server SFTP version
170
+ *
171
+ * @var Integer
172
+ * @see Net_SFTP::_initChannel()
173
+ * @access private
174
+ */
175
+ var $version;
176
+
177
+ /**
178
+ * Current working directory
179
+ *
180
+ * @var String
181
+ * @see Net_SFTP::_realpath()
182
+ * @see Net_SFTP::chdir()
183
+ * @access private
184
+ */
185
+ var $pwd = false;
186
+
187
+ /**
188
+ * Packet Type Log
189
+ *
190
+ * @see Net_SFTP::getLog()
191
+ * @var Array
192
+ * @access private
193
+ */
194
+ var $packet_type_log = array();
195
+
196
+ /**
197
+ * Packet Log
198
+ *
199
+ * @see Net_SFTP::getLog()
200
+ * @var Array
201
+ * @access private
202
+ */
203
+ var $packet_log = array();
204
+
205
+ /**
206
+ * Error information
207
+ *
208
+ * @see Net_SFTP::getSFTPErrors()
209
+ * @see Net_SFTP::getLastSFTPError()
210
+ * @var String
211
+ * @access private
212
+ */
213
+ var $errors = array();
214
+
215
+ /**
216
+ * Default Constructor.
217
+ *
218
+ * Connects to an SFTP server
219
+ *
220
+ * @param String $host
221
+ * @param optional Integer $port
222
+ * @param optional Integer $timeout
223
+ * @return Net_SFTP
224
+ * @access public
225
+ */
226
+ function Net_SFTP($host, $port = 22, $timeout = 10)
227
+ {
228
+ parent::Net_SSH2($host, $port, $timeout);
229
+ $this->packet_types = array(
230
+ 1 => 'NET_SFTP_INIT',
231
+ 2 => 'NET_SFTP_VERSION',
232
+ /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
233
+ SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
234
+ pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
235
+ 3 => 'NET_SFTP_OPEN',
236
+ 4 => 'NET_SFTP_CLOSE',
237
+ 5 => 'NET_SFTP_READ',
238
+ 6 => 'NET_SFTP_WRITE',
239
+ 8 => 'NET_SFTP_FSTAT',
240
+ 9 => 'NET_SFTP_SETSTAT',
241
+ 11 => 'NET_SFTP_OPENDIR',
242
+ 12 => 'NET_SFTP_READDIR',
243
+ 13 => 'NET_SFTP_REMOVE',
244
+ 14 => 'NET_SFTP_MKDIR',
245
+ 15 => 'NET_SFTP_RMDIR',
246
+ 16 => 'NET_SFTP_REALPATH',
247
+ 17 => 'NET_SFTP_STAT',
248
+ /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
249
+ SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
250
+ pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
251
+ 18 => 'NET_SFTP_RENAME',
252
+
253
+ 101=> 'NET_SFTP_STATUS',
254
+ 102=> 'NET_SFTP_HANDLE',
255
+ /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
256
+ SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
257
+ pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
258
+ 103=> 'NET_SFTP_DATA',
259
+ 104=> 'NET_SFTP_NAME',
260
+ 105=> 'NET_SFTP_ATTRS',
261
+
262
+ 200=> 'NET_SFTP_EXTENDED'
263
+ );
264
+ $this->status_codes = array(
265
+ 0 => 'NET_SFTP_STATUS_OK',
266
+ 1 => 'NET_SFTP_STATUS_EOF',
267
+ 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
268
+ 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
269
+ 4 => 'NET_SFTP_STATUS_FAILURE',
270
+ 5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
271
+ 6 => 'NET_SFTP_STATUS_NO_CONNECTION',
272
+ 7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
273
+ 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED'
274
+ );
275
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
276
+ // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
277
+ $this->attributes = array(
278
+ 0x00000001 => 'NET_SFTP_ATTR_SIZE',
279
+ 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
280
+ 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
281
+ 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
282
+ -1 => 'NET_SFTP_ATTR_EXTENDED' // unpack('N', "\xFF\xFF\xFF\xFF") == array(1 => int(-1))
283
+ );
284
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
285
+ // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
286
+ // the array for that $this->open5_flags and similarily alter the constant names.
287
+ $this->open_flags = array(
288
+ 0x00000001 => 'NET_SFTP_OPEN_READ',
289
+ 0x00000002 => 'NET_SFTP_OPEN_WRITE',
290
+ 0x00000008 => 'NET_SFTP_OPEN_CREATE',
291
+ 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE'
292
+ );
293
+ $this->_define_array(
294
+ $this->packet_types,
295
+ $this->status_codes,
296
+ $this->attributes,
297
+ $this->open_flags
298
+ );
299
+ }
300
+
301
+ /**
302
+ * Login
303
+ *
304
+ * @param String $username
305
+ * @param optional String $password
306
+ * @return Boolean
307
+ * @access public
308
+ */
309
+ function login($username, $password = '')
310
+ {
311
+ if (!parent::login($username, $password)) {
312
+ return false;
313
+ }
314
+
315
+ $this->window_size_client_to_server[NET_SFTP_CHANNEL] = $this->window_size;
316
+
317
+ $packet = pack('CNa*N3',
318
+ NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SFTP_CHANNEL, $this->window_size, 0x4000);
319
+
320
+ if (!$this->_send_binary_packet($packet)) {
321
+ return false;
322
+ }
323
+
324
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
325
+
326
+ $response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
327
+ if ($response === false) {
328
+ return false;
329
+ }
330
+
331
+ $packet = pack('CNNa*CNa*',
332
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('subsystem'), 'subsystem', 1, strlen('sftp'), 'sftp');
333
+ if (!$this->_send_binary_packet($packet)) {
334
+ return false;
335
+ }
336
+
337
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
338
+
339
+ $response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
340
+ if ($response === false) {
341
+ return false;
342
+ }
343
+
344
+ $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
345
+
346
+ if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
347
+ return false;
348
+ }
349
+
350
+ $response = $this->_get_sftp_packet();
351
+ if ($this->packet_type != NET_SFTP_VERSION) {
352
+ user_error('Expected SSH_FXP_VERSION', E_USER_NOTICE);
353
+ return false;
354
+ }
355
+
356
+ extract(unpack('Nversion', $this->_string_shift($response, 4)));
357
+ $this->version = $version;
358
+ while (!empty($response)) {
359
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
360
+ $key = $this->_string_shift($response, $length);
361
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
362
+ $value = $this->_string_shift($response, $length);
363
+ $this->extensions[$key] = $value;
364
+ }
365
+
366
+ /*
367
+ SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
368
+ however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
369
+ not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
370
+ one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
371
+ 'newline@vandyke.com' would.
372
+ */
373
+ /*
374
+ if (isset($this->extensions['newline@vandyke.com'])) {
375
+ $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
376
+ unset($this->extensions['newline@vandyke.com']);
377
+ }
378
+ */
379
+
380
+ $this->request_id = 1;
381
+
382
+ /*
383
+ A Note on SFTPv4/5/6 support:
384
+ <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:
385
+
386
+ "If the client wishes to interoperate with servers that support noncontiguous version
387
+ numbers it SHOULD send '3'"
388
+
389
+ Given that the server only sends its version number after the client has already done so, the above
390
+ seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the
391
+ most popular.
392
+
393
+ <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;
394
+
395
+ "If the server did not send the "versions" extension, or the version-from-list was not included, the
396
+ server MAY send a status response describing the failure, but MUST then close the channel without
397
+ processing any further requests."
398
+
399
+ So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
400
+ a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements
401
+ v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
402
+ in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what Net_SFTP would do is close the
403
+ channel and reopen it with a new and updated SSH_FXP_INIT packet.
404
+ */
405
+ if ($this->version != 3) {
406
+ return false;
407
+ }
408
+
409
+ $this->pwd = $this->_realpath('.');
410
+
411
+ return true;
412
+ }
413
+
414
+ /**
415
+ * Returns the current directory name
416
+ *
417
+ * @return Mixed
418
+ * @access public
419
+ */
420
+ function pwd()
421
+ {
422
+ return $this->pwd;
423
+ }
424
+
425
+ /**
426
+ * Canonicalize the Server-Side Path Name
427
+ *
428
+ * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns
429
+ * the absolute (canonicalized) path. If $mode is set to NET_SFTP_CONFIRM_DIR (as opposed to NET_SFTP_CONFIRM_NONE,
430
+ * which is what it is set to by default), false is returned if $dir is not a valid directory.
431
+ *
432
+ * @see Net_SFTP::chdir()
433
+ * @param String $dir
434
+ * @param optional Integer $mode
435
+ * @return Mixed
436
+ * @access private
437
+ */
438
+ function _realpath($dir)
439
+ {
440
+ /*
441
+ "This protocol represents file names as strings. File names are
442
+ assumed to use the slash ('/') character as a directory separator.
443
+
444
+ File names starting with a slash are "absolute", and are relative to
445
+ the root of the file system. Names starting with any other character
446
+ are relative to the user's default directory (home directory). Note
447
+ that identifying the user is assumed to take place outside of this
448
+ protocol."
449
+
450
+ -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-6
451
+ */
452
+ $file = '';
453
+ if ($this->pwd !== false) {
454
+ // if the SFTP server returned the canonicalized path even for non-existant files this wouldn't be necessary
455
+ // on OpenSSH it isn't necessary but on other SFTP servers it is. that and since the specs say nothing on
456
+ // the subject, we'll go ahead and work around it with the following.
457
+ if ($dir[strlen($dir) - 1] != '/') {
458
+ $file = basename($dir);
459
+ $dir = dirname($dir);
460
+ }
461
+
462
+ if ($dir == '.' || $dir == $this->pwd) {
463
+ return $this->pwd . $file;
464
+ }
465
+
466
+ if ($dir[0] != '/') {
467
+ $dir = $this->pwd . '/' . $dir;
468
+ }
469
+ // on the surface it seems like maybe resolving a path beginning with / is unnecessary, but such paths
470
+ // can contain .'s and ..'s just like any other. we could parse those out as appropriate or we can let
471
+ // the server do it. we'll do the latter.
472
+ }
473
+
474
+ /*
475
+ that SSH_FXP_REALPATH returns SSH_FXP_NAME does not necessarily mean that anything actually exists at the
476
+ specified path. generally speaking, no attributes are returned with this particular SSH_FXP_NAME packet
477
+ regardless of whether or not a file actually exists. and in SFTPv3, the longname field and the filename
478
+ field match for this particular SSH_FXP_NAME packet. for other SSH_FXP_NAME packets, this will likely
479
+ not be the case, but for this one, it is.
480
+ */
481
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
482
+ if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($dir), $dir))) {
483
+ return false;
484
+ }
485
+
486
+ $response = $this->_get_sftp_packet();
487
+ switch ($this->packet_type) {
488
+ case NET_SFTP_NAME:
489
+ // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
490
+ // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
491
+ // at is the first part and that part is defined the same in SFTP versions 3 through 6.
492
+ $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
493
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
494
+ $realpath = $this->_string_shift($response, $length);
495
+ break;
496
+ case NET_SFTP_STATUS:
497
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
498
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
499
+ return false;
500
+ default:
501
+ user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS', E_USER_NOTICE);
502
+ return false;
503
+ }
504
+
505
+ // if $this->pwd isn't set than the only thing $realpath could be is for '.', which is pretty much guaranteed to
506
+ // be a bonafide directory
507
+ return $realpath . '/' . $file;
508
+ }
509
+
510
+ /**
511
+ * Changes the current directory
512
+ *
513
+ * @param String $dir
514
+ * @return Boolean
515
+ * @access public
516
+ */
517
+ function chdir($dir)
518
+ {
519
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
520
+ return false;
521
+ }
522
+
523
+ if ($dir[strlen($dir) - 1] != '/') {
524
+ $dir.= '/';
525
+ }
526
+ $dir = $this->_realpath($dir);
527
+
528
+ // confirm that $dir is, in fact, a valid directory
529
+ if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
530
+ return false;
531
+ }
532
+
533
+ // see Net_SFTP::nlist() for a more thorough explanation of the following
534
+ $response = $this->_get_sftp_packet();
535
+ switch ($this->packet_type) {
536
+ case NET_SFTP_HANDLE:
537
+ $handle = substr($response, 4);
538
+ break;
539
+ case NET_SFTP_STATUS:
540
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
541
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
542
+ return false;
543
+ default:
544
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
545
+ return false;
546
+ }
547
+
548
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
549
+ return false;
550
+ }
551
+
552
+ $response = $this->_get_sftp_packet();
553
+ if ($this->packet_type != NET_SFTP_STATUS) {
554
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
555
+ return false;
556
+ }
557
+
558
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
559
+ if ($status != NET_SFTP_STATUS_OK) {
560
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
561
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
562
+ return false;
563
+ }
564
+
565
+ $this->pwd = $dir;
566
+ return true;
567
+ }
568
+
569
+ /**
570
+ * Returns a list of files in the given directory
571
+ *
572
+ * @param optional String $dir
573
+ * @return Mixed
574
+ * @access public
575
+ */
576
+ function nlist($dir = '.')
577
+ {
578
+ return $this->_list($dir, false);
579
+ }
580
+
581
+ /**
582
+ * Returns a list of files in the given directory
583
+ *
584
+ * @param optional String $dir
585
+ * @return Mixed
586
+ * @access public
587
+ */
588
+ function rawlist($dir = '.')
589
+ {
590
+ return $this->_list($dir, true);
591
+ }
592
+
593
+ function _list($dir, $raw = true)
594
+ {
595
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
596
+ return false;
597
+ }
598
+
599
+ $dir = $this->_realpath($dir);
600
+ if ($dir === false) {
601
+ return false;
602
+ }
603
+
604
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
605
+ if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
606
+ return false;
607
+ }
608
+
609
+ $response = $this->_get_sftp_packet();
610
+ switch ($this->packet_type) {
611
+ case NET_SFTP_HANDLE:
612
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
613
+ // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
614
+ // represent the length of the string and leave it at that
615
+ $handle = substr($response, 4);
616
+ break;
617
+ case NET_SFTP_STATUS:
618
+ // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
619
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
620
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
621
+ return false;
622
+ default:
623
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
624
+ return false;
625
+ }
626
+
627
+ $contents = array();
628
+ while (true) {
629
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
630
+ // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
631
+ // SSH_MSG_CHANNEL_DATA messages is not known to me.
632
+ if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) {
633
+ return false;
634
+ }
635
+
636
+ $response = $this->_get_sftp_packet();
637
+ switch ($this->packet_type) {
638
+ case NET_SFTP_NAME:
639
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
640
+ for ($i = 0; $i < $count; $i++) {
641
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
642
+ $shortname = $this->_string_shift($response, $length);
643
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
644
+ $this->_string_shift($response, $length); // SFTPv4+ drop this field - the "longname" field
645
+ $attributes = $this->_parseAttributes($response); // we also don't care about the attributes
646
+ if (!$raw) {
647
+ $contents[] = $shortname;
648
+ } else {
649
+ $contents[$shortname] = $attributes;
650
+ }
651
+ // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
652
+ // final SSH_FXP_STATUS packet should tell us that, already.
653
+ }
654
+ break;
655
+ case NET_SFTP_STATUS:
656
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
657
+ if ($status != NET_SFTP_STATUS_EOF) {
658
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
659
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
660
+ return false;
661
+ }
662
+ break 2;
663
+ default:
664
+ user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS', E_USER_NOTICE);
665
+ return false;
666
+ }
667
+ }
668
+
669
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
670
+ return false;
671
+ }
672
+
673
+ // "The client MUST release all resources associated with the handle regardless of the status."
674
+ // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
675
+ $response = $this->_get_sftp_packet();
676
+ if ($this->packet_type != NET_SFTP_STATUS) {
677
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
678
+ return false;
679
+ }
680
+
681
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
682
+ if ($status != NET_SFTP_STATUS_OK) {
683
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
684
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
685
+ return false;
686
+ }
687
+
688
+ return $contents;
689
+ }
690
+
691
+ /**
692
+ * Returns the file size, in bytes, or false, on failure
693
+ *
694
+ * Files larger than 4GB will show up as being exactly 4GB.
695
+ *
696
+ * @param optional String $dir
697
+ * @return Mixed
698
+ * @access public
699
+ */
700
+ function size($filename)
701
+ {
702
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
703
+ return false;
704
+ }
705
+
706
+ $filename = $this->_realpath($filename);
707
+ if ($filename === false) {
708
+ return false;
709
+ }
710
+
711
+ // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
712
+ $packet = pack('Na*', strlen($filename), $filename);
713
+ if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
714
+ return false;
715
+ }
716
+
717
+ $response = $this->_get_sftp_packet();
718
+ switch ($this->packet_type) {
719
+ case NET_SFTP_ATTRS:
720
+ $attrs = $this->_parseAttributes($response);
721
+ return $attrs['size'];
722
+ case NET_SFTP_STATUS:
723
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
724
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
725
+ return false;
726
+ }
727
+
728
+ user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
729
+ return false;
730
+ }
731
+
732
+ /**
733
+ * Set permissions on a file.
734
+ *
735
+ * Returns the new file permissions on success or FALSE on error.
736
+ *
737
+ * @param Integer $mode
738
+ * @param String $filename
739
+ * @return Mixed
740
+ * @access public
741
+ */
742
+ function chmod($mode, $filename)
743
+ {
744
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
745
+ return false;
746
+ }
747
+
748
+ $filename = $this->_realpath($filename);
749
+ if ($filename === false) {
750
+ return false;
751
+ }
752
+
753
+ // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
754
+ // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
755
+ $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
756
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
757
+ return false;
758
+ }
759
+
760
+ /*
761
+ "Because some systems must use separate system calls to set various attributes, it is possible that a failure
762
+ response will be returned, but yet some of the attributes may be have been successfully modified. If possible,
763
+ servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
764
+
765
+ -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
766
+ */
767
+ $response = $this->_get_sftp_packet();
768
+ if ($this->packet_type != NET_SFTP_STATUS) {
769
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
770
+ return false;
771
+ }
772
+
773
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
774
+ if ($status != NET_SFTP_STATUS_EOF) {
775
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
776
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
777
+ }
778
+
779
+ // rather than return what the permissions *should* be, we'll return what they actually are. this will also
780
+ // tell us if the file actually exists.
781
+ // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
782
+ $packet = pack('Na*', strlen($filename), $filename);
783
+ if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
784
+ return false;
785
+ }
786
+
787
+ $response = $this->_get_sftp_packet();
788
+ switch ($this->packet_type) {
789
+ case NET_SFTP_ATTRS:
790
+ $attrs = $this->_parseAttributes($response);
791
+ return $attrs['permissions'];
792
+ case NET_SFTP_STATUS:
793
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
794
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
795
+ return false;
796
+ }
797
+
798
+ user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
799
+ return false;
800
+ }
801
+
802
+ /**
803
+ * Creates a directory.
804
+ *
805
+ * @param String $dir
806
+ * @return Boolean
807
+ * @access public
808
+ */
809
+ function mkdir($dir)
810
+ {
811
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
812
+ return false;
813
+ }
814
+
815
+ $dir = $this->_realpath(rtrim($dir, '/'));
816
+ if ($dir === false) {
817
+ return false;
818
+ }
819
+
820
+ // by not providing any permissions, hopefully the server will use the logged in users umask - their
821
+ // default permissions.
822
+ if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*N', strlen($dir), $dir, 0))) {
823
+ return false;
824
+ }
825
+
826
+ $response = $this->_get_sftp_packet();
827
+ if ($this->packet_type != NET_SFTP_STATUS) {
828
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
829
+ return false;
830
+ }
831
+
832
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
833
+ if ($status != NET_SFTP_STATUS_OK) {
834
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
835
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
836
+ return false;
837
+ }
838
+
839
+ return true;
840
+ }
841
+
842
+ /**
843
+ * Removes a directory.
844
+ *
845
+ * @param String $dir
846
+ * @return Boolean
847
+ * @access public
848
+ */
849
+ function rmdir($dir)
850
+ {
851
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
852
+ return false;
853
+ }
854
+
855
+ $dir = $this->_realpath($dir);
856
+ if ($dir === false) {
857
+ return false;
858
+ }
859
+
860
+ if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) {
861
+ return false;
862
+ }
863
+
864
+ $response = $this->_get_sftp_packet();
865
+ if ($this->packet_type != NET_SFTP_STATUS) {
866
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
867
+ return false;
868
+ }
869
+
870
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
871
+ if ($status != NET_SFTP_STATUS_OK) {
872
+ // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
873
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
874
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
875
+ return false;
876
+ }
877
+
878
+ return true;
879
+ }
880
+
881
+ /**
882
+ * Uploads a file to the SFTP server.
883
+ *
884
+ * By default, Net_SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
885
+ * So, for example, if you set $data to 'filename.ext' and then do Net_SFTP::get(), you will get a file, twelve bytes
886
+ * long, containing 'filename.ext' as its contents.
887
+ *
888
+ * Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will
889
+ * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
890
+ * large $remote_file will be, as well.
891
+ *
892
+ * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
893
+ * care of that, yourself.
894
+ *
895
+ * @param String $remote_file
896
+ * @param String $data
897
+ * @param optional Integer $flags
898
+ * @return Boolean
899
+ * @access public
900
+ * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode().
901
+ */
902
+ function put($remote_file, $data, $mode = NET_SFTP_STRING)
903
+ {
904
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
905
+ return false;
906
+ }
907
+
908
+ $remote_file = $this->_realpath($remote_file);
909
+ if ($remote_file === false) {
910
+ return false;
911
+ }
912
+
913
+ $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_TRUNCATE, 0);
914
+ if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
915
+ return false;
916
+ }
917
+
918
+ $response = $this->_get_sftp_packet();
919
+ switch ($this->packet_type) {
920
+ case NET_SFTP_HANDLE:
921
+ $handle = substr($response, 4);
922
+ break;
923
+ case NET_SFTP_STATUS:
924
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
925
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
926
+ return false;
927
+ default:
928
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
929
+ return false;
930
+ }
931
+
932
+ $initialize = true;
933
+
934
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
935
+ if ($mode == NET_SFTP_LOCAL_FILE) {
936
+ if (!is_file($data)) {
937
+ user_error("$data is not a valid file", E_USER_NOTICE);
938
+ return false;
939
+ }
940
+ $fp = fopen($data, 'rb');
941
+ if (!$fp) {
942
+ return false;
943
+ }
944
+ $sent = 0;
945
+ $size = filesize($data);
946
+ } else {
947
+ $sent = 0;
948
+ $size = strlen($data);
949
+ }
950
+
951
+ $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
952
+
953
+ $sftp_packet_size = 34000; // PuTTY uses 4096
954
+ $i = 0;
955
+ while ($sent < $size) {
956
+ $temp = $mode == NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size);
957
+ $packet = pack('Na*N3a*', strlen($handle), $handle, 0, $sent, strlen($temp), $temp);
958
+ if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) {
959
+ fclose($fp);
960
+ return false;
961
+ }
962
+ $sent+= strlen($temp);
963
+
964
+ $i++;
965
+
966
+ if ($i == 50) {
967
+ if (!$this->_read_put_responses($i)) {
968
+ $i = 0;
969
+ break;
970
+ }
971
+ $i = 0;
972
+ }
973
+ }
974
+
975
+ $this->_read_put_responses($i);
976
+
977
+ if ($mode == NET_SFTP_LOCAL_FILE) {
978
+ fclose($fp);
979
+ }
980
+
981
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
982
+ return false;
983
+ }
984
+
985
+ $response = $this->_get_sftp_packet();
986
+ if ($this->packet_type != NET_SFTP_STATUS) {
987
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
988
+ return false;
989
+ }
990
+
991
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
992
+ if ($status != NET_SFTP_STATUS_OK) {
993
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
994
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
995
+ return false;
996
+ }
997
+
998
+ return true;
999
+ }
1000
+
1001
+ /**
1002
+ * Reads multiple successive SSH_FXP_WRITE responses
1003
+ *
1004
+ * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i
1005
+ * SSH_FXP_WRITEs, in succession, and then reading $i responses.
1006
+ *
1007
+ * @param Integer $i
1008
+ * @return Boolean
1009
+ * @access private
1010
+ */
1011
+ function _read_put_responses($i)
1012
+ {
1013
+ while ($i--) {
1014
+ $response = $this->_get_sftp_packet();
1015
+ if ($this->packet_type != NET_SFTP_STATUS) {
1016
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1017
+ return false;
1018
+ }
1019
+
1020
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1021
+ if ($status != NET_SFTP_STATUS_OK) {
1022
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1023
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1024
+ break;
1025
+ }
1026
+ }
1027
+
1028
+ return $i < 0;
1029
+ }
1030
+
1031
+ /**
1032
+ * Downloads a file from the SFTP server.
1033
+ *
1034
+ * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
1035
+ * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
1036
+ * operation
1037
+ *
1038
+ * @param String $remote_file
1039
+ * @param optional String $local_file
1040
+ * @return Mixed
1041
+ * @access public
1042
+ */
1043
+ function get($remote_file, $local_file = false)
1044
+ {
1045
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1046
+ return false;
1047
+ }
1048
+
1049
+ $remote_file = $this->_realpath($remote_file);
1050
+ if ($remote_file === false) {
1051
+ return false;
1052
+ }
1053
+
1054
+ $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
1055
+ if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1056
+ return false;
1057
+ }
1058
+
1059
+ $response = $this->_get_sftp_packet();
1060
+ switch ($this->packet_type) {
1061
+ case NET_SFTP_HANDLE:
1062
+ $handle = substr($response, 4);
1063
+ break;
1064
+ case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
1065
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
1066
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1067
+ return false;
1068
+ default:
1069
+ user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
1070
+ return false;
1071
+ }
1072
+
1073
+ $packet = pack('Na*', strlen($handle), $handle);
1074
+ if (!$this->_send_sftp_packet(NET_SFTP_FSTAT, $packet)) {
1075
+ return false;
1076
+ }
1077
+
1078
+ $response = $this->_get_sftp_packet();
1079
+ switch ($this->packet_type) {
1080
+ case NET_SFTP_ATTRS:
1081
+ $attrs = $this->_parseAttributes($response);
1082
+ break;
1083
+ case NET_SFTP_STATUS:
1084
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
1085
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1086
+ return false;
1087
+ default:
1088
+ user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
1089
+ return false;
1090
+ }
1091
+
1092
+ if ($local_file !== false) {
1093
+ $fp = fopen($local_file, 'wb');
1094
+ if (!$fp) {
1095
+ return false;
1096
+ }
1097
+ } else {
1098
+ $content = '';
1099
+ }
1100
+
1101
+ $read = 0;
1102
+ while ($read < $attrs['size']) {
1103
+ $packet = pack('Na*N3', strlen($handle), $handle, 0, $read, 1 << 20);
1104
+ if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
1105
+ return false;
1106
+ }
1107
+
1108
+ $response = $this->_get_sftp_packet();
1109
+ switch ($this->packet_type) {
1110
+ case NET_SFTP_DATA:
1111
+ $temp = substr($response, 4);
1112
+ $read+= strlen($temp);
1113
+ if ($local_file === false) {
1114
+ $content.= $temp;
1115
+ } else {
1116
+ fputs($fp, $temp);
1117
+ }
1118
+ break;
1119
+ case NET_SFTP_STATUS:
1120
+ extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
1121
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1122
+ break 2;
1123
+ default:
1124
+ user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS', E_USER_NOTICE);
1125
+ return false;
1126
+ }
1127
+ }
1128
+
1129
+ if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
1130
+ return false;
1131
+ }
1132
+
1133
+ $response = $this->_get_sftp_packet();
1134
+ if ($this->packet_type != NET_SFTP_STATUS) {
1135
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1136
+ return false;
1137
+ }
1138
+
1139
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1140
+ if ($status != NET_SFTP_STATUS_OK) {
1141
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1142
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1143
+ return false;
1144
+ }
1145
+
1146
+ if (isset($content)) {
1147
+ return $content;
1148
+ }
1149
+
1150
+ fclose($fp);
1151
+ return true;
1152
+ }
1153
+
1154
+ /**
1155
+ * Deletes a file on the SFTP server.
1156
+ *
1157
+ * @param String $path
1158
+ * @return Boolean
1159
+ * @access public
1160
+ */
1161
+ function delete($path)
1162
+ {
1163
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1164
+ return false;
1165
+ }
1166
+
1167
+ $remote_file = $this->_realpath($path);
1168
+ if ($path === false) {
1169
+ return false;
1170
+ }
1171
+
1172
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
1173
+ if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) {
1174
+ return false;
1175
+ }
1176
+
1177
+ $response = $this->_get_sftp_packet();
1178
+ if ($this->packet_type != NET_SFTP_STATUS) {
1179
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1180
+ return false;
1181
+ }
1182
+
1183
+ // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
1184
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1185
+ if ($status != NET_SFTP_STATUS_OK) {
1186
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1187
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1188
+ return false;
1189
+ }
1190
+
1191
+ return true;
1192
+ }
1193
+
1194
+ /**
1195
+ * Renames a file or a directory on the SFTP server
1196
+ *
1197
+ * @param String $oldname
1198
+ * @param String $newname
1199
+ * @return Boolean
1200
+ * @access public
1201
+ */
1202
+ function rename($oldname, $newname)
1203
+ {
1204
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1205
+ return false;
1206
+ }
1207
+
1208
+ $oldname = $this->_realpath($oldname);
1209
+ $newname = $this->_realpath($newname);
1210
+ if ($oldname === false || $newname === false) {
1211
+ return false;
1212
+ }
1213
+
1214
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
1215
+ $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
1216
+ if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
1217
+ return false;
1218
+ }
1219
+
1220
+ $response = $this->_get_sftp_packet();
1221
+ if ($this->packet_type != NET_SFTP_STATUS) {
1222
+ user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
1223
+ return false;
1224
+ }
1225
+
1226
+ // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
1227
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1228
+ if ($status != NET_SFTP_STATUS_OK) {
1229
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1230
+ $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
1231
+ return false;
1232
+ }
1233
+
1234
+ return true;
1235
+ }
1236
+
1237
+ /**
1238
+ * Parse Attributes
1239
+ *
1240
+ * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
1241
+ *
1242
+ * @param String $response
1243
+ * @return Array
1244
+ * @access private
1245
+ */
1246
+ function _parseAttributes(&$response)
1247
+ {
1248
+ $attr = array();
1249
+ extract(unpack('Nflags', $this->_string_shift($response, 4)));
1250
+ // SFTPv4+ have a type field (a byte) that follows the above flag field
1251
+ foreach ($this->attributes as $key => $value) {
1252
+ switch ($flags & $key) {
1253
+ case NET_SFTP_ATTR_SIZE: // 0x00000001
1254
+ // size is represented by a 64-bit integer, so we perhaps ought to be doing the following:
1255
+ // $attr['size'] = new Math_BigInteger($this->_string_shift($response, 8), 256);
1256
+ // of course, you shouldn't be using Net_SFTP to transfer files that are in excess of 4GB
1257
+ // (0xFFFFFFFF bytes), anyway. as such, we'll just represent all file sizes that are bigger than
1258
+ // 4GB as being 4GB.
1259
+ extract(unpack('Nupper/Nsize', $this->_string_shift($response, 8)));
1260
+ if ($upper) {
1261
+ $attr['size'] = 0xFFFFFFFF;
1262
+ } else {
1263
+ $attr['size'] = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
1264
+ }
1265
+ break;
1266
+ case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
1267
+ $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
1268
+ break;
1269
+ case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
1270
+ $attr+= unpack('Npermissions', $this->_string_shift($response, 4));
1271
+ break;
1272
+ case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
1273
+ $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
1274
+ break;
1275
+ case NET_SFTP_ATTR_EXTENDED: // 0x80000000
1276
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
1277
+ for ($i = 0; $i < $count; $i++) {
1278
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1279
+ $key = $this->_string_shift($response, $length);
1280
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1281
+ $attr[$key] = $this->_string_shift($response, $length);
1282
+ }
1283
+ }
1284
+ }
1285
+ return $attr;
1286
+ }
1287
+
1288
+ /**
1289
+ * Sends SFTP Packets
1290
+ *
1291
+ * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
1292
+ *
1293
+ * @param Integer $type
1294
+ * @param String $data
1295
+ * @see Net_SFTP::_get_sftp_packet()
1296
+ * @see Net_SSH2::_send_channel_packet()
1297
+ * @return Boolean
1298
+ * @access private
1299
+ */
1300
+ function _send_sftp_packet($type, $data)
1301
+ {
1302
+ $packet = $this->request_id !== false ?
1303
+ pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) :
1304
+ pack('NCa*', strlen($data) + 1, $type, $data);
1305
+
1306
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1307
+ $result = $this->_send_channel_packet(NET_SFTP_CHANNEL, $packet);
1308
+ $stop = strtok(microtime(), ' ') + strtok('');
1309
+
1310
+ if (defined('NET_SFTP_LOGGING')) {
1311
+ $this->packet_type_log[] = '-> ' . $this->packet_types[$type] .
1312
+ ' (' . round($stop - $start, 4) . 's)';
1313
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
1314
+ $this->packet_log[] = $data;
1315
+ }
1316
+ }
1317
+
1318
+ return $result;
1319
+ }
1320
+
1321
+ /**
1322
+ * Receives SFTP Packets
1323
+ *
1324
+ * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
1325
+ *
1326
+ * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present.
1327
+ * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA
1328
+ * messages containing one SFTP packet.
1329
+ *
1330
+ * @see Net_SFTP::_send_sftp_packet()
1331
+ * @return String
1332
+ * @access private
1333
+ */
1334
+ function _get_sftp_packet()
1335
+ {
1336
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1337
+
1338
+ // SFTP packet length
1339
+ while (strlen($this->packet_buffer) < 4) {
1340
+ $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL);
1341
+ if (is_bool($temp)) {
1342
+ $this->packet_type = false;
1343
+ $this->packet_buffer = '';
1344
+ return false;
1345
+ }
1346
+ $this->packet_buffer.= $temp;
1347
+ }
1348
+ extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4)));
1349
+ $tempLength = $length;
1350
+ $tempLength-= strlen($this->packet_buffer);
1351
+
1352
+ // SFTP packet type and data payload
1353
+ while ($tempLength > 0) {
1354
+ $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL);
1355
+ if (is_bool($temp)) {
1356
+ $this->packet_type = false;
1357
+ $this->packet_buffer = '';
1358
+ return false;
1359
+ }
1360
+ $this->packet_buffer.= $temp;
1361
+ $tempLength-= strlen($temp);
1362
+ }
1363
+
1364
+ $stop = strtok(microtime(), ' ') + strtok('');
1365
+
1366
+ $this->packet_type = ord($this->_string_shift($this->packet_buffer));
1367
+
1368
+ if ($this->request_id !== false) {
1369
+ $this->_string_shift($this->packet_buffer, 4); // remove the request id
1370
+ $length-= 5; // account for the request id and the packet type
1371
+ } else {
1372
+ $length-= 1; // account for the packet type
1373
+ }
1374
+
1375
+ $packet = $this->_string_shift($this->packet_buffer, $length);
1376
+
1377
+ if (defined('NET_SFTP_LOGGING')) {
1378
+ $this->packet_type_log[] = '<- ' . $this->packet_types[$this->packet_type] .
1379
+ ' (' . round($stop - $start, 4) . 's)';
1380
+ if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
1381
+ $this->packet_log[] = $packet;
1382
+ }
1383
+ }
1384
+
1385
+ return $packet;
1386
+ }
1387
+
1388
+ /**
1389
+ * Returns a log of the packets that have been sent and received.
1390
+ *
1391
+ * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
1392
+ *
1393
+ * @access public
1394
+ * @return String or Array
1395
+ */
1396
+ function getSFTPLog()
1397
+ {
1398
+ if (!defined('NET_SFTP_LOGGING')) {
1399
+ return false;
1400
+ }
1401
+
1402
+ switch (NET_SFTP_LOGGING) {
1403
+ case NET_SFTP_LOG_COMPLEX:
1404
+ return $this->_format_log($this->packet_log, $this->packet_type_log);
1405
+ break;
1406
+ //case NET_SFTP_LOG_SIMPLE:
1407
+ default:
1408
+ return $this->packet_type_log;
1409
+ }
1410
+ }
1411
+
1412
+ /**
1413
+ * Returns all errors
1414
+ *
1415
+ * @return String
1416
+ * @access public
1417
+ */
1418
+ function getSFTPErrors()
1419
+ {
1420
+ return $this->sftp_errors;
1421
+ }
1422
+
1423
+ /**
1424
+ * Returns the last error
1425
+ *
1426
+ * @return String
1427
+ * @access public
1428
+ */
1429
+ function getLastSFTPError()
1430
+ {
1431
+ return $this->sftp_errors[count($this->sftp_errors) - 1];
1432
+ }
1433
+
1434
+ /**
1435
+ * Get supported SFTP versions
1436
+ *
1437
+ * @return Array
1438
+ * @access public
1439
+ */
1440
+ function getSupportedVersions()
1441
+ {
1442
+ $temp = array('version' => $this->version);
1443
+ if (isset($this->extensions['versions'])) {
1444
+ $temp['extensions'] = $this->extensions['versions'];
1445
+ }
1446
+ return $temp;
1447
+ }
1448
+
1449
+ /**
1450
+ * Disconnect
1451
+ *
1452
+ * @param Integer $reason
1453
+ * @return Boolean
1454
+ * @access private
1455
+ */
1456
+ function _disconnect($reason)
1457
+ {
1458
+ $this->pwd = false;
1459
+ parent::_disconnect($reason);
1460
+ }
1461
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/Net/SSH1.php ADDED
@@ -0,0 +1,1159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of SSHv1.
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Here's a short example of how to use this library:
10
+ * <code>
11
+ * <?php
12
+ * include('Net/SSH1.php');
13
+ *
14
+ * $ssh = new Net_SSH1('www.domain.tld');
15
+ * if (!$ssh->login('username', 'password')) {
16
+ * exit('Login Failed');
17
+ * }
18
+ *
19
+ * while (true) {
20
+ * echo $ssh->interactiveRead();
21
+ *
22
+ * $read = array(STDIN);
23
+ * $write = $except = NULL;
24
+ * if (stream_select($read, $write, $except, 0)) {
25
+ * $ssh->interactiveWrite(fread(STDIN, 1));
26
+ * }
27
+ * }
28
+ * ?>
29
+ * </code>
30
+ *
31
+ * Here's another short example:
32
+ * <code>
33
+ * <?php
34
+ * include('Net/SSH1.php');
35
+ *
36
+ * $ssh = new Net_SSH1('www.domain.tld');
37
+ * if (!$ssh->login('username', 'password')) {
38
+ * exit('Login Failed');
39
+ * }
40
+ *
41
+ * echo $ssh->exec('ls -la');
42
+ * ?>
43
+ * </code>
44
+ *
45
+ * More information on the SSHv1 specification can be found by reading
46
+ * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
47
+ *
48
+ * LICENSE: This library is free software; you can redistribute it and/or
49
+ * modify it under the terms of the GNU Lesser General Public
50
+ * License as published by the Free Software Foundation; either
51
+ * version 2.1 of the License, or (at your option) any later version.
52
+ *
53
+ * This library is distributed in the hope that it will be useful,
54
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
55
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
56
+ * Lesser General Public License for more details.
57
+ *
58
+ * You should have received a copy of the GNU Lesser General Public
59
+ * License along with this library; if not, write to the Free Software
60
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
61
+ * MA 02111-1307 USA
62
+ *
63
+ * @category Net
64
+ * @package Net_SSH1
65
+ * @author Jim Wigginton <terrafrost@php.net>
66
+ * @copyright MMVII Jim Wigginton
67
+ * @license http://www.gnu.org/licenses/lgpl.txt
68
+ * @version $Id: SSH1.php,v 1.15 2010/03/22 22:01:38 terrafrost Exp $
69
+ * @link http://phpseclib.sourceforge.net
70
+ */
71
+
72
+ /**
73
+ * Include Math_BigInteger
74
+ *
75
+ * Used to do RSA encryption.
76
+ */
77
+ require_once('phpseclib/Math/BigInteger.php');
78
+
79
+ /**
80
+ * Include Crypt_Null
81
+ */
82
+ //require_once('Crypt/Null.php');
83
+
84
+ /**
85
+ * Include Crypt_DES
86
+ */
87
+ require_once('phpseclib/Crypt/DES.php');
88
+
89
+ /**
90
+ * Include Crypt_TripleDES
91
+ */
92
+ require_once('phpseclib/Crypt/TripleDES.php');
93
+
94
+ /**
95
+ * Include Crypt_RC4
96
+ */
97
+ require_once('phpseclib/Crypt/RC4.php');
98
+
99
+ /**
100
+ * Include Crypt_Random
101
+ */
102
+ require_once('phpseclib/Crypt/Random.php');
103
+
104
+ /**#@+
105
+ * Protocol Flags
106
+ *
107
+ * @access private
108
+ */
109
+ define('NET_SSH1_MSG_DISCONNECT', 1);
110
+ define('NET_SSH1_SMSG_PUBLIC_KEY', 2);
111
+ define('NET_SSH1_CMSG_SESSION_KEY', 3);
112
+ define('NET_SSH1_CMSG_USER', 4);
113
+ define('NET_SSH1_CMSG_AUTH_PASSWORD', 9);
114
+ define('NET_SSH1_CMSG_REQUEST_PTY', 10);
115
+ define('NET_SSH1_CMSG_EXEC_SHELL', 12);
116
+ define('NET_SSH1_CMSG_EXEC_CMD', 13);
117
+ define('NET_SSH1_SMSG_SUCCESS', 14);
118
+ define('NET_SSH1_SMSG_FAILURE', 15);
119
+ define('NET_SSH1_CMSG_STDIN_DATA', 16);
120
+ define('NET_SSH1_SMSG_STDOUT_DATA', 17);
121
+ define('NET_SSH1_SMSG_STDERR_DATA', 18);
122
+ define('NET_SSH1_SMSG_EXITSTATUS', 20);
123
+ define('NET_SSH1_CMSG_EXIT_CONFIRMATION', 33);
124
+ /**#@-*/
125
+
126
+ /**#@+
127
+ * Encryption Methods
128
+ *
129
+ * @see Net_SSH1::getSupportedCiphers()
130
+ * @access public
131
+ */
132
+ /**
133
+ * No encryption
134
+ *
135
+ * Not supported.
136
+ */
137
+ define('NET_SSH1_CIPHER_NONE', 0);
138
+ /**
139
+ * IDEA in CFB mode
140
+ *
141
+ * Not supported.
142
+ */
143
+ define('NET_SSH1_CIPHER_IDEA', 1);
144
+ /**
145
+ * DES in CBC mode
146
+ */
147
+ define('NET_SSH1_CIPHER_DES', 2);
148
+ /**
149
+ * Triple-DES in CBC mode
150
+ *
151
+ * All implementations are required to support this
152
+ */
153
+ define('NET_SSH1_CIPHER_3DES', 3);
154
+ /**
155
+ * TRI's Simple Stream encryption CBC
156
+ *
157
+ * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, does define it (see cipher.h),
158
+ * although it doesn't use it (see cipher.c)
159
+ */
160
+ define('NET_SSH1_CIPHER_BROKEN_TSS', 4);
161
+ /**
162
+ * RC4
163
+ *
164
+ * Not supported.
165
+ *
166
+ * @internal According to the SSH1 specs:
167
+ *
168
+ * "The first 16 bytes of the session key are used as the key for
169
+ * the server to client direction. The remaining 16 bytes are used
170
+ * as the key for the client to server direction. This gives
171
+ * independent 128-bit keys for each direction."
172
+ *
173
+ * This library currently only supports encryption when the same key is being used for both directions. This is
174
+ * because there's only one $crypto object. Two could be added ($encrypt and $decrypt, perhaps).
175
+ */
176
+ define('NET_SSH1_CIPHER_RC4', 5);
177
+ /**
178
+ * Blowfish
179
+ *
180
+ * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, defines it (see cipher.h) and
181
+ * uses it (see cipher.c)
182
+ */
183
+ define('NET_SSH1_CIPHER_BLOWFISH', 6);
184
+ /**#@-*/
185
+
186
+ /**#@+
187
+ * Authentication Methods
188
+ *
189
+ * @see Net_SSH1::getSupportedAuthentications()
190
+ * @access public
191
+ */
192
+ /**
193
+ * .rhosts or /etc/hosts.equiv
194
+ */
195
+ define('NET_SSH1_AUTH_RHOSTS', 1);
196
+ /**
197
+ * pure RSA authentication
198
+ */
199
+ define('NET_SSH1_AUTH_RSA', 2);
200
+ /**
201
+ * password authentication
202
+ *
203
+ * This is the only method that is supported by this library.
204
+ */
205
+ define('NET_SSH1_AUTH_PASSWORD', 3);
206
+ /**
207
+ * .rhosts with RSA host authentication
208
+ */
209
+ define('NET_SSH1_AUTH_RHOSTS_RSA', 4);
210
+ /**#@-*/
211
+
212
+ /**#@+
213
+ * Terminal Modes
214
+ *
215
+ * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
216
+ * @access private
217
+ */
218
+ define('NET_SSH1_TTY_OP_END', 0);
219
+ /**#@-*/
220
+
221
+ /**
222
+ * The Response Type
223
+ *
224
+ * @see Net_SSH1::_get_binary_packet()
225
+ * @access private
226
+ */
227
+ define('NET_SSH1_RESPONSE_TYPE', 1);
228
+
229
+ /**
230
+ * The Response Data
231
+ *
232
+ * @see Net_SSH1::_get_binary_packet()
233
+ * @access private
234
+ */
235
+ define('NET_SSH1_RESPONSE_DATA', 2);
236
+
237
+ /**#@+
238
+ * Execution Bitmap Masks
239
+ *
240
+ * @see Net_SSH1::bitmap
241
+ * @access private
242
+ */
243
+ define('NET_SSH1_MASK_CONSTRUCTOR', 0x00000001);
244
+ define('NET_SSH1_MASK_LOGIN', 0x00000002);
245
+ define('NET_SSH1_MASK_SHELL', 0x00000004);
246
+ /**#@-*/
247
+
248
+ /**
249
+ * Pure-PHP implementation of SSHv1.
250
+ *
251
+ * @author Jim Wigginton <terrafrost@php.net>
252
+ * @version 0.1.0
253
+ * @access public
254
+ * @package Net_SSH1
255
+ */
256
+ class Net_SSH1 {
257
+ /**
258
+ * The SSH identifier
259
+ *
260
+ * @var String
261
+ * @access private
262
+ */
263
+ var $identifier = 'SSH-1.5-phpseclib';
264
+
265
+ /**
266
+ * The Socket Object
267
+ *
268
+ * @var Object
269
+ * @access private
270
+ */
271
+ var $fsock;
272
+
273
+ /**
274
+ * The cryptography object
275
+ *
276
+ * @var Object
277
+ * @access private
278
+ */
279
+ var $crypto = false;
280
+
281
+ /**
282
+ * Execution Bitmap
283
+ *
284
+ * The bits that are set reprsent functions that have been called already. This is used to determine
285
+ * if a requisite function has been successfully executed. If not, an error should be thrown.
286
+ *
287
+ * @var Integer
288
+ * @access private
289
+ */
290
+ var $bitmap = 0;
291
+
292
+ /**
293
+ * The Server Key Public Exponent
294
+ *
295
+ * Logged for debug purposes
296
+ *
297
+ * @see Net_SSH1::getServerKeyPublicExponent()
298
+ * @var String
299
+ * @access private
300
+ */
301
+ var $server_key_public_exponent;
302
+
303
+ /**
304
+ * The Server Key Public Modulus
305
+ *
306
+ * Logged for debug purposes
307
+ *
308
+ * @see Net_SSH1::getServerKeyPublicModulus()
309
+ * @var String
310
+ * @access private
311
+ */
312
+ var $server_key_public_modulus;
313
+
314
+ /**
315
+ * The Host Key Public Exponent
316
+ *
317
+ * Logged for debug purposes
318
+ *
319
+ * @see Net_SSH1::getHostKeyPublicExponent()
320
+ * @var String
321
+ * @access private
322
+ */
323
+ var $host_key_public_exponent;
324
+
325
+ /**
326
+ * The Host Key Public Modulus
327
+ *
328
+ * Logged for debug purposes
329
+ *
330
+ * @see Net_SSH1::getHostKeyPublicModulus()
331
+ * @var String
332
+ * @access private
333
+ */
334
+ var $host_key_public_modulus;
335
+
336
+ /**
337
+ * Supported Ciphers
338
+ *
339
+ * Logged for debug purposes
340
+ *
341
+ * @see Net_SSH1::getSupportedCiphers()
342
+ * @var Array
343
+ * @access private
344
+ */
345
+ var $supported_ciphers = array(
346
+ NET_SSH1_CIPHER_NONE => 'No encryption',
347
+ NET_SSH1_CIPHER_IDEA => 'IDEA in CFB mode',
348
+ NET_SSH1_CIPHER_DES => 'DES in CBC mode',
349
+ NET_SSH1_CIPHER_3DES => 'Triple-DES in CBC mode',
350
+ NET_SSH1_CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC',
351
+ NET_SSH1_CIPHER_RC4 => 'RC4',
352
+ NET_SSH1_CIPHER_BLOWFISH => 'Blowfish'
353
+ );
354
+
355
+ /**
356
+ * Supported Authentications
357
+ *
358
+ * Logged for debug purposes
359
+ *
360
+ * @see Net_SSH1::getSupportedAuthentications()
361
+ * @var Array
362
+ * @access private
363
+ */
364
+ var $supported_authentications = array(
365
+ NET_SSH1_AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv',
366
+ NET_SSH1_AUTH_RSA => 'pure RSA authentication',
367
+ NET_SSH1_AUTH_PASSWORD => 'password authentication',
368
+ NET_SSH1_AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication'
369
+ );
370
+
371
+ /**
372
+ * Server Identification
373
+ *
374
+ * @see Net_SSH1::getServerIdentification()
375
+ * @var String
376
+ * @access private
377
+ */
378
+ var $server_identification = '';
379
+
380
+ /**
381
+ * Default Constructor.
382
+ *
383
+ * Connects to an SSHv1 server
384
+ *
385
+ * @param String $host
386
+ * @param optional Integer $port
387
+ * @param optional Integer $timeout
388
+ * @param optional Integer $cipher
389
+ * @return Net_SSH1
390
+ * @access public
391
+ */
392
+ function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES)
393
+ {
394
+ $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
395
+ if (!$this->fsock) {
396
+ user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
397
+ return;
398
+ }
399
+
400
+ $this->server_identification = $init_line = fgets($this->fsock, 255);
401
+ if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
402
+ user_error('Can only connect to SSH servers', E_USER_NOTICE);
403
+ return;
404
+ }
405
+ if ($parts[1][0] != 1) {
406
+ user_error("Cannot connect to SSH $parts[1] servers", E_USER_NOTICE);
407
+ return;
408
+ }
409
+
410
+ fputs($this->fsock, $this->identifier."\r\n");
411
+
412
+ $response = $this->_get_binary_packet();
413
+ if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
414
+ user_error('Expected SSH_SMSG_PUBLIC_KEY', E_USER_NOTICE);
415
+ return;
416
+ }
417
+
418
+ $anti_spoofing_cookie = $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 8);
419
+
420
+ $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
421
+
422
+ $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
423
+ $server_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
424
+ $this->server_key_public_exponent = $server_key_public_exponent;
425
+
426
+ $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
427
+ $server_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
428
+ $this->server_key_public_modulus = $server_key_public_modulus;
429
+
430
+ $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
431
+
432
+ $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
433
+ $host_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
434
+ $this->host_key_public_exponent = $host_key_public_exponent;
435
+
436
+ $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
437
+ $host_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
438
+ $this->host_key_public_modulus = $host_key_public_modulus;
439
+
440
+ $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);
441
+
442
+ // get a list of the supported ciphers
443
+ extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)));
444
+ foreach ($this->supported_ciphers as $mask=>$name) {
445
+ if (($supported_ciphers_mask & (1 << $mask)) == 0) {
446
+ unset($this->supported_ciphers[$mask]);
447
+ }
448
+ }
449
+
450
+ // get a list of the supported authentications
451
+ extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)));
452
+ foreach ($this->supported_authentications as $mask=>$name) {
453
+ if (($supported_authentications_mask & (1 << $mask)) == 0) {
454
+ unset($this->supported_authentications[$mask]);
455
+ }
456
+ }
457
+
458
+ $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
459
+
460
+ $session_key = '';
461
+ for ($i = 0; $i < 32; $i++) {
462
+ $session_key.= chr(crypt_random(0, 255));
463
+ }
464
+ $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));
465
+
466
+ if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
467
+ $double_encrypted_session_key = $this->_rsa_crypt(
468
+ $double_encrypted_session_key,
469
+ array(
470
+ $server_key_public_exponent,
471
+ $server_key_public_modulus
472
+ )
473
+ );
474
+ $double_encrypted_session_key = $this->_rsa_crypt(
475
+ $double_encrypted_session_key,
476
+ array(
477
+ $host_key_public_exponent,
478
+ $host_key_public_modulus
479
+ )
480
+ );
481
+ } else {
482
+ $double_encrypted_session_key = $this->_rsa_crypt(
483
+ $double_encrypted_session_key,
484
+ array(
485
+ $host_key_public_exponent,
486
+ $host_key_public_modulus
487
+ )
488
+ );
489
+ $double_encrypted_session_key = $this->_rsa_crypt(
490
+ $double_encrypted_session_key,
491
+ array(
492
+ $server_key_public_exponent,
493
+ $server_key_public_modulus
494
+ )
495
+ );
496
+ }
497
+
498
+ $cipher = isset($this->supported_ciphers[$cipher]) ? $cipher : NET_SSH1_CIPHER_3DES;
499
+ $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);
500
+
501
+ if (!$this->_send_binary_packet($data)) {
502
+ user_error('Error sending SSH_CMSG_SESSION_KEY', E_USER_NOTICE);
503
+ return;
504
+ }
505
+
506
+ switch ($cipher) {
507
+ //case NET_SSH1_CIPHER_NONE:
508
+ // $this->crypto = new Crypt_Null();
509
+ // break;
510
+ case NET_SSH1_CIPHER_DES:
511
+ $this->crypto = new Crypt_DES();
512
+ $this->crypto->disablePadding();
513
+ $this->crypto->enableContinuousBuffer();
514
+ $this->crypto->setKey(substr($session_key, 0, 8));
515
+ break;
516
+ case NET_SSH1_CIPHER_3DES:
517
+ $this->crypto = new Crypt_TripleDES(CRYPT_DES_MODE_3CBC);
518
+ $this->crypto->disablePadding();
519
+ $this->crypto->enableContinuousBuffer();
520
+ $this->crypto->setKey(substr($session_key, 0, 24));
521
+ break;
522
+ //case NET_SSH1_CIPHER_RC4:
523
+ // $this->crypto = new Crypt_RC4();
524
+ // $this->crypto->enableContinuousBuffer();
525
+ // $this->crypto->setKey(substr($session_key, 0, 16));
526
+ // break;
527
+ }
528
+
529
+ $response = $this->_get_binary_packet();
530
+
531
+ if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
532
+ user_error('Expected SSH_SMSG_SUCCESS', E_USER_NOTICE);
533
+ return;
534
+ }
535
+
536
+ $this->bitmap = NET_SSH1_MASK_CONSTRUCTOR;
537
+ }
538
+
539
+ /**
540
+ * Login
541
+ *
542
+ * @param String $username
543
+ * @param optional String $password
544
+ * @return Boolean
545
+ * @access public
546
+ */
547
+ function login($username, $password = '')
548
+ {
549
+ if (!($this->bitmap & NET_SSH1_MASK_CONSTRUCTOR)) {
550
+ return false;
551
+ }
552
+
553
+ $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);
554
+
555
+ if (!$this->_send_binary_packet($data)) {
556
+ user_error('Error sending SSH_CMSG_USER', E_USER_NOTICE);
557
+ return false;
558
+ }
559
+
560
+ $response = $this->_get_binary_packet();
561
+
562
+ if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
563
+ $this->bitmap |= NET_SSH1_MASK_LOGIN;
564
+ return true;
565
+ } else if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
566
+ user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE', E_USER_NOTICE);
567
+ return false;
568
+ }
569
+
570
+ $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);
571
+
572
+ if (!$this->_send_binary_packet($data)) {
573
+ user_error('Error sending SSH_CMSG_AUTH_PASSWORD', E_USER_NOTICE);
574
+ return false;
575
+ }
576
+
577
+ $response = $this->_get_binary_packet();
578
+
579
+ if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
580
+ $this->bitmap |= NET_SSH1_MASK_LOGIN;
581
+ return true;
582
+ } else if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
583
+ return false;
584
+ } else {
585
+ user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE', E_USER_NOTICE);
586
+ return false;
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Executes a command on a non-interactive shell, returns the output, and quits.
592
+ *
593
+ * An SSH1 server will close the connection after a command has been executed on a non-interactive shell. SSH2
594
+ * servers don't, however, this isn't an SSH2 client. The way this works, on the server, is by initiating a
595
+ * shell with the -s option, as discussed in the following links:
596
+ *
597
+ * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html}
598
+ * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html}
599
+ *
600
+ * To execute further commands, a new Net_SSH1 object will need to be created.
601
+ *
602
+ * Returns false on failure and the output, otherwise.
603
+ *
604
+ * @see Net_SSH1::interactiveRead()
605
+ * @see Net_SSH1::interactiveWrite()
606
+ * @param String $cmd
607
+ * @return mixed
608
+ * @access public
609
+ */
610
+ function exec($cmd)
611
+ {
612
+ if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
613
+ user_error('Operation disallowed prior to login()', E_USER_NOTICE);
614
+ return false;
615
+ }
616
+
617
+ // connect using the sample parameters in protocol-1.5.txt.
618
+ // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
619
+ // terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell.
620
+ $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END);
621
+
622
+ if (!$this->_send_binary_packet($data)) {
623
+ user_error('Error sending SSH_CMSG_REQUEST_PTY', E_USER_NOTICE);
624
+ return false;
625
+ }
626
+
627
+ $response = $this->_get_binary_packet();
628
+
629
+ if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
630
+ user_error('Expected SSH_SMSG_SUCCESS', E_USER_NOTICE);
631
+ return false;
632
+ }
633
+
634
+ $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);
635
+
636
+ if (!$this->_send_binary_packet($data)) {
637
+ user_error('Error sending SSH_CMSG_EXEC_CMD', E_USER_NOTICE);
638
+ return false;
639
+ }
640
+
641
+ $output = '';
642
+ $response = $this->_get_binary_packet();
643
+
644
+ do {
645
+ $output.= substr($response[NET_SSH1_RESPONSE_DATA], 4);
646
+ $response = $this->_get_binary_packet();
647
+ } while ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
648
+
649
+ $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
650
+
651
+ // i don't think it's really all that important if this packet gets sent or not.
652
+ $this->_send_binary_packet($data);
653
+
654
+ fclose($this->fsock);
655
+
656
+ // reset the execution bitmap - a new Net_SSH1 object needs to be created.
657
+ $this->bitmap = 0;
658
+
659
+ return $output;
660
+ }
661
+
662
+ /**
663
+ * Creates an interactive shell
664
+ *
665
+ * @see Net_SSH1::interactiveRead()
666
+ * @see Net_SSH1::interactiveWrite()
667
+ * @return Boolean
668
+ * @access private
669
+ */
670
+ function _initShell()
671
+ {
672
+ $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END);
673
+
674
+ if (!$this->_send_binary_packet($data)) {
675
+ user_error('Error sending SSH_CMSG_REQUEST_PTY', E_USER_NOTICE);
676
+ return false;
677
+ }
678
+
679
+ $response = $this->_get_binary_packet();
680
+
681
+ if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
682
+ user_error('Expected SSH_SMSG_SUCCESS', E_USER_NOTICE);
683
+ return false;
684
+ }
685
+
686
+ $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
687
+
688
+ if (!$this->_send_binary_packet($data)) {
689
+ user_error('Error sending SSH_CMSG_EXEC_SHELL', E_USER_NOTICE);
690
+ return false;
691
+ }
692
+
693
+ $this->bitmap |= NET_SSH1_MASK_SHELL;
694
+
695
+ //stream_set_blocking($this->fsock, 0);
696
+
697
+ return true;
698
+ }
699
+
700
+ /**
701
+ * Inputs a command into an interactive shell.
702
+ *
703
+ * @see Net_SSH1::interactiveRead()
704
+ * @param String $cmd
705
+ * @return Boolean
706
+ * @access public
707
+ */
708
+ function interactiveWrite($cmd)
709
+ {
710
+ if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
711
+ user_error('Operation disallowed prior to login()', E_USER_NOTICE);
712
+ return false;
713
+ }
714
+
715
+ if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
716
+ user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
717
+ return false;
718
+ }
719
+
720
+ $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);
721
+
722
+ if (!$this->_send_binary_packet($data)) {
723
+ user_error('Error sending SSH_CMSG_STDIN', E_USER_NOTICE);
724
+ return false;
725
+ }
726
+
727
+ return true;
728
+ }
729
+
730
+ /**
731
+ * Reads the output of an interactive shell.
732
+ *
733
+ * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like
734
+ * "[00m", you're seeing ANSI escape codes. According to
735
+ * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT
736
+ * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user,
737
+ * there's not going to be much recourse.
738
+ *
739
+ * @see Net_SSH1::interactiveRead()
740
+ * @return String
741
+ * @access public
742
+ */
743
+ function interactiveRead()
744
+ {
745
+ if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
746
+ user_error('Operation disallowed prior to login()', E_USER_NOTICE);
747
+ return false;
748
+ }
749
+
750
+ if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
751
+ user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
752
+ return false;
753
+ }
754
+
755
+ $read = array($this->fsock);
756
+ $write = $except = null;
757
+ if (stream_select($read, $write, $except, 0)) {
758
+ $response = $this->_get_binary_packet();
759
+ return substr($response[NET_SSH1_RESPONSE_DATA], 4);
760
+ } else {
761
+ return '';
762
+ }
763
+ }
764
+
765
+ /**
766
+ * Disconnect
767
+ *
768
+ * @access public
769
+ */
770
+ function disconnect()
771
+ {
772
+ $this->_disconnect();
773
+ }
774
+
775
+ /**
776
+ * Destructor.
777
+ *
778
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
779
+ * disconnect().
780
+ *
781
+ * @access public
782
+ */
783
+ function __destruct()
784
+ {
785
+ $this->_disconnect();
786
+ }
787
+
788
+ /**
789
+ * Disconnect
790
+ *
791
+ * @param String $msg
792
+ * @access private
793
+ */
794
+ function _disconnect($msg = 'Client Quit')
795
+ {
796
+ if ($this->bitmap) {
797
+ $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
798
+ $this->_send_binary_packet($data);
799
+ fclose($this->fsock);
800
+ $this->bitmap = 0;
801
+ }
802
+ }
803
+
804
+ /**
805
+ * Gets Binary Packets
806
+ *
807
+ * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info.
808
+ *
809
+ * Also, this function could be improved upon by adding detection for the following exploit:
810
+ * http://www.securiteam.com/securitynews/5LP042K3FY.html
811
+ *
812
+ * @see Net_SSH1::_send_binary_packet()
813
+ * @return Array
814
+ * @access private
815
+ */
816
+ function _get_binary_packet()
817
+ {
818
+ if (feof($this->fsock)) {
819
+ //user_error('connection closed prematurely', E_USER_NOTICE);
820
+ return false;
821
+ }
822
+
823
+ $temp = unpack('Nlength', fread($this->fsock, 4));
824
+
825
+ $padding_length = 8 - ($temp['length'] & 7);
826
+ $length = $temp['length'] + $padding_length;
827
+
828
+ $raw = fread($this->fsock, $length);
829
+
830
+ if ($this->crypto !== false) {
831
+ $raw = $this->crypto->decrypt($raw);
832
+ }
833
+
834
+ $padding = substr($raw, 0, $padding_length);
835
+ $type = $raw[$padding_length];
836
+ $data = substr($raw, $padding_length + 1, -4);
837
+
838
+ $temp = unpack('Ncrc', substr($raw, -4));
839
+
840
+ //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
841
+ // user_error('Bad CRC in packet from server', E_USER_NOTICE);
842
+ // return false;
843
+ //}
844
+
845
+ return array(
846
+ NET_SSH1_RESPONSE_TYPE => ord($type),
847
+ NET_SSH1_RESPONSE_DATA => $data
848
+ );
849
+ }
850
+
851
+ /**
852
+ * Sends Binary Packets
853
+ *
854
+ * Returns true on success, false on failure.
855
+ *
856
+ * @see Net_SSH1::_get_binary_packet()
857
+ * @param String $data
858
+ * @return Boolean
859
+ * @access private
860
+ */
861
+ function _send_binary_packet($data) {
862
+ if (feof($this->fsock)) {
863
+ //user_error('connection closed prematurely', E_USER_NOTICE);
864
+ return false;
865
+ }
866
+
867
+ $length = strlen($data) + 4;
868
+
869
+ $padding_length = 8 - ($length & 7);
870
+ $padding = '';
871
+ for ($i = 0; $i < $padding_length; $i++) {
872
+ $padding.= chr(crypt_random(0, 255));
873
+ }
874
+
875
+ $data = $padding . $data;
876
+ $data.= pack('N', $this->_crc($data));
877
+
878
+ if ($this->crypto !== false) {
879
+ $data = $this->crypto->encrypt($data);
880
+ }
881
+
882
+ $packet = pack('Na*', $length, $data);
883
+
884
+ return strlen($packet) == fputs($this->fsock, $packet);
885
+ }
886
+
887
+ /**
888
+ * Cyclic Redundancy Check (CRC)
889
+ *
890
+ * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so
891
+ * we've reimplemented it. A more detailed discussion of the differences can be found after
892
+ * $crc_lookup_table's initialization.
893
+ *
894
+ * @see Net_SSH1::_get_binary_packet()
895
+ * @see Net_SSH1::_send_binary_packet()
896
+ * @param String $data
897
+ * @return Integer
898
+ * @access private
899
+ */
900
+ function _crc($data)
901
+ {
902
+ static $crc_lookup_table = array(
903
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
904
+ 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
905
+ 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
906
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
907
+ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
908
+ 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
909
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
910
+ 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
911
+ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
912
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
913
+ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
914
+ 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
915
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
916
+ 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
917
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
918
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
919
+ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
920
+ 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
921
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
922
+ 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
923
+ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
924
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
925
+ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
926
+ 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
927
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
928
+ 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
929
+ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
930
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
931
+ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
932
+ 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
933
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
934
+ 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
935
+ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
936
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
937
+ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
938
+ 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
939
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
940
+ 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
941
+ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
942
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
943
+ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
944
+ 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
945
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
946
+ 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
947
+ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
948
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
949
+ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
950
+ 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
951
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
952
+ 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
953
+ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
954
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
955
+ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
956
+ 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
957
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
958
+ 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
959
+ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
960
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
961
+ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
962
+ 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
963
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
964
+ 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
965
+ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
966
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
967
+ );
968
+
969
+ // For this function to yield the same output as PHP's crc32 function, $crc would have to be
970
+ // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is.
971
+ $crc = 0x00000000;
972
+ $length = strlen($data);
973
+
974
+ for ($i=0;$i<$length;$i++) {
975
+ // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all
976
+ // be zero. PHP, unfortunately, doesn't always do this. 0x80000000 >> 8, as an example,
977
+ // yields 0xFF800000 - not 0x00800000. The following link elaborates:
978
+ // http://www.php.net/manual/en/language.operators.bitwise.php#57281
979
+ $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
980
+ }
981
+
982
+ // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with
983
+ // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would.
984
+ return $crc;
985
+ }
986
+
987
+ /**
988
+ * String Shift
989
+ *
990
+ * Inspired by array_shift
991
+ *
992
+ * @param String $string
993
+ * @param optional Integer $index
994
+ * @return String
995
+ * @access private
996
+ */
997
+ function _string_shift(&$string, $index = 1)
998
+ {
999
+ $substr = substr($string, 0, $index);
1000
+ $string = substr($string, $index);
1001
+ return $substr;
1002
+ }
1003
+
1004
+ /**
1005
+ * RSA Encrypt
1006
+ *
1007
+ * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e
1008
+ * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1. Could just make anything that
1009
+ * calls this call modexp, instead, but I think this makes things clearer, maybe...
1010
+ *
1011
+ * @see Net_SSH1::Net_SSH1()
1012
+ * @param Math_BigInteger $m
1013
+ * @param Array $key
1014
+ * @return Math_BigInteger
1015
+ * @access private
1016
+ */
1017
+ function _rsa_crypt($m, $key)
1018
+ {
1019
+ /*
1020
+ if (!class_exists('Crypt_RSA')) {
1021
+ require_once('Crypt/RSA.php');
1022
+ }
1023
+
1024
+ $rsa = new Crypt_RSA();
1025
+ $rsa->loadKey($key, CRYPT_RSA_PUBLIC_FORMAT_RAW);
1026
+ $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
1027
+ return $rsa->encrypt($m);
1028
+ */
1029
+
1030
+ // To quote from protocol-1.5.txt:
1031
+ // The most significant byte (which is only partial as the value must be
1032
+ // less than the public modulus, which is never a power of two) is zero.
1033
+ //
1034
+ // The next byte contains the value 2 (which stands for public-key
1035
+ // encrypted data in the PKCS standard [PKCS#1]). Then, there are non-
1036
+ // zero random bytes to fill any unused space, a zero byte, and the data
1037
+ // to be encrypted in the least significant bytes, the last byte of the
1038
+ // data in the least significant byte.
1039
+
1040
+ // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation",
1041
+ // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL:
1042
+ // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
1043
+ $temp = chr(0) . chr(2);
1044
+ $modulus = $key[1]->toBytes();
1045
+ $length = strlen($modulus) - strlen($m) - 3;
1046
+ for ($i = 0; $i < $length; $i++) {
1047
+ $temp.= chr(crypt_random(1, 255));
1048
+ }
1049
+ $temp.= chr(0) . $m;
1050
+
1051
+ $m = new Math_BigInteger($temp, 256);
1052
+ $m = $m->modPow($key[0], $key[1]);
1053
+
1054
+ return $m->toBytes();
1055
+ }
1056
+
1057
+ /**
1058
+ * Return the server key public exponent
1059
+ *
1060
+ * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
1061
+ * the raw bytes. This behavior is similar to PHP's md5() function.
1062
+ *
1063
+ * @param optional Boolean $raw_output
1064
+ * @return String
1065
+ * @access public
1066
+ */
1067
+ function getServerKeyPublicExponent($raw_output = false)
1068
+ {
1069
+ return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString();
1070
+ }
1071
+
1072
+ /**
1073
+ * Return the server key public modulus
1074
+ *
1075
+ * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
1076
+ * the raw bytes. This behavior is similar to PHP's md5() function.
1077
+ *
1078
+ * @param optional Boolean $raw_output
1079
+ * @return String
1080
+ * @access public
1081
+ */
1082
+ function getServerKeyPublicModulus($raw_output = false)
1083
+ {
1084
+ return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString();
1085
+ }
1086
+
1087
+ /**
1088
+ * Return the host key public exponent
1089
+ *
1090
+ * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
1091
+ * the raw bytes. This behavior is similar to PHP's md5() function.
1092
+ *
1093
+ * @param optional Boolean $raw_output
1094
+ * @return String
1095
+ * @access public
1096
+ */
1097
+ function getHostKeyPublicExponent($raw_output = false)
1098
+ {
1099
+ return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString();
1100
+ }
1101
+
1102
+ /**
1103
+ * Return the host key public modulus
1104
+ *
1105
+ * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
1106
+ * the raw bytes. This behavior is similar to PHP's md5() function.
1107
+ *
1108
+ * @param optional Boolean $raw_output
1109
+ * @return String
1110
+ * @access public
1111
+ */
1112
+ function getHostKeyPublicModulus($raw_output = false)
1113
+ {
1114
+ return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString();
1115
+ }
1116
+
1117
+ /**
1118
+ * Return a list of ciphers supported by SSH1 server.
1119
+ *
1120
+ * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
1121
+ * is set to true, returns, instead, an array of constants. ie. instead of array('Triple-DES in CBC mode'), you'll
1122
+ * get array(NET_SSH1_CIPHER_3DES).
1123
+ *
1124
+ * @param optional Boolean $raw_output
1125
+ * @return Array
1126
+ * @access public
1127
+ */
1128
+ function getSupportedCiphers($raw_output = false)
1129
+ {
1130
+ return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers);
1131
+ }
1132
+
1133
+ /**
1134
+ * Return a list of authentications supported by SSH1 server.
1135
+ *
1136
+ * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
1137
+ * is set to true, returns, instead, an array of constants. ie. instead of array('password authentication'), you'll
1138
+ * get array(NET_SSH1_AUTH_PASSWORD).
1139
+ *
1140
+ * @param optional Boolean $raw_output
1141
+ * @return Array
1142
+ * @access public
1143
+ */
1144
+ function getSupportedAuthentications($raw_output = false)
1145
+ {
1146
+ return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications);
1147
+ }
1148
+
1149
+ /**
1150
+ * Return the server identification.
1151
+ *
1152
+ * @return String
1153
+ * @access public
1154
+ */
1155
+ function getServerIdentification()
1156
+ {
1157
+ return rtrim($this->server_identification);
1158
+ }
1159
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/Net/SSH2.php ADDED
@@ -0,0 +1,2302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Pure-PHP implementation of SSHv2.
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * Here are some examples of how to use this library:
10
+ * <code>
11
+ * <?php
12
+ * include('Net/SSH2.php');
13
+ *
14
+ * $ssh = new Net_SSH2('www.domain.tld');
15
+ * if (!$ssh->login('username', 'password')) {
16
+ * exit('Login Failed');
17
+ * }
18
+ *
19
+ * echo $ssh->exec('pwd');
20
+ * echo $ssh->exec('ls -la');
21
+ * ?>
22
+ * </code>
23
+ *
24
+ * <code>
25
+ * <?php
26
+ * include('Crypt/RSA.php');
27
+ * include('Net/SSH2.php');
28
+ *
29
+ * $key = new Crypt_RSA();
30
+ * //$key->setPassword('whatever');
31
+ * $key->loadKey(file_get_contents('privatekey'));
32
+ *
33
+ * $ssh = new Net_SSH2('www.domain.tld');
34
+ * if (!$ssh->login('username', $key)) {
35
+ * exit('Login Failed');
36
+ * }
37
+ *
38
+ * echo $ssh->exec('pwd');
39
+ * echo $ssh->exec('ls -la');
40
+ * ?>
41
+ * </code>
42
+ *
43
+ * LICENSE: This library is free software; you can redistribute it and/or
44
+ * modify it under the terms of the GNU Lesser General Public
45
+ * License as published by the Free Software Foundation; either
46
+ * version 2.1 of the License, or (at your option) any later version.
47
+ *
48
+ * This library is distributed in the hope that it will be useful,
49
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
50
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
51
+ * Lesser General Public License for more details.
52
+ *
53
+ * You should have received a copy of the GNU Lesser General Public
54
+ * License along with this library; if not, write to the Free Software
55
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
56
+ * MA 02111-1307 USA
57
+ *
58
+ * @category Net
59
+ * @package Net_SSH2
60
+ * @author Jim Wigginton <terrafrost@php.net>
61
+ * @copyright MMVII Jim Wigginton
62
+ * @license http://www.gnu.org/licenses/lgpl.txt
63
+ * @version $Id: SSH2.php,v 1.46 2010/04/27 21:29:36 terrafrost Exp $
64
+ * @link http://phpseclib.sourceforge.net
65
+ */
66
+
67
+ /**
68
+ * Include Math_BigInteger
69
+ *
70
+ * Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
71
+ */
72
+ require_once('phpseclib/Math/BigInteger.php');
73
+
74
+ /**
75
+ * Include Crypt_Random
76
+ */
77
+ require_once('phpseclib/Crypt/Random.php');
78
+
79
+ /**
80
+ * Include Crypt_Hash
81
+ */
82
+ require_once('phpseclib/Crypt/Hash.php');
83
+
84
+ /**
85
+ * Include Crypt_TripleDES
86
+ */
87
+ require_once('phpseclib/Crypt/TripleDES.php');
88
+
89
+ /**
90
+ * Include Crypt_RC4
91
+ */
92
+ require_once('phpseclib/Crypt/RC4.php');
93
+
94
+ /**
95
+ * Include Crypt_AES
96
+ */
97
+ require_once('phpseclib/Crypt/AES.php');
98
+
99
+ /**#@+
100
+ * Execution Bitmap Masks
101
+ *
102
+ * @see Net_SSH2::bitmap
103
+ * @access private
104
+ */
105
+ define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
106
+ define('NET_SSH2_MASK_LOGIN', 0x00000002);
107
+ /**#@-*/
108
+
109
+ /**#@+
110
+ * Channel constants
111
+ *
112
+ * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
113
+ * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
114
+ * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
115
+ * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
116
+ * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
117
+ * The 'recipient channel' is the channel number given in the original
118
+ * open request, and 'sender channel' is the channel number allocated by
119
+ * the other side.
120
+ *
121
+ * @see Net_SSH2::_send_channel_packet()
122
+ * @see Net_SSH2::_get_channel_packet()
123
+ * @access private
124
+ */
125
+ define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
126
+ /**#@-*/
127
+
128
+ /**#@+
129
+ * @access public
130
+ * @see Net_SSH2::getLog()
131
+ */
132
+ /**
133
+ * Returns the message numbers
134
+ */
135
+ define('NET_SSH2_LOG_SIMPLE', 1);
136
+ /**
137
+ * Returns the message content
138
+ */
139
+ define('NET_SSH2_LOG_COMPLEX', 2);
140
+ /**#@-*/
141
+
142
+ /**
143
+ * Pure-PHP implementation of SSHv2.
144
+ *
145
+ * @author Jim Wigginton <terrafrost@php.net>
146
+ * @version 0.1.0
147
+ * @access public
148
+ * @package Net_SSH2
149
+ */
150
+ class Net_SSH2 {
151
+ /**
152
+ * The SSH identifier
153
+ *
154
+ * @var String
155
+ * @access private
156
+ */
157
+ var $identifier = 'SSH-2.0-phpseclib_0.2';
158
+
159
+ /**
160
+ * The Socket Object
161
+ *
162
+ * @var Object
163
+ * @access private
164
+ */
165
+ var $fsock;
166
+
167
+ /**
168
+ * Execution Bitmap
169
+ *
170
+ * The bits that are set reprsent functions that have been called already. This is used to determine
171
+ * if a requisite function has been successfully executed. If not, an error should be thrown.
172
+ *
173
+ * @var Integer
174
+ * @access private
175
+ */
176
+ var $bitmap = 0;
177
+
178
+ /**
179
+ * Error information
180
+ *
181
+ * @see Net_SSH2::getErrors()
182
+ * @see Net_SSH2::getLastError()
183
+ * @var String
184
+ * @access private
185
+ */
186
+ var $errors = array();
187
+
188
+ /**
189
+ * Server Identifier
190
+ *
191
+ * @see Net_SSH2::getServerIdentification()
192
+ * @var String
193
+ * @access private
194
+ */
195
+ var $server_identifier = '';
196
+
197
+ /**
198
+ * Key Exchange Algorithms
199
+ *
200
+ * @see Net_SSH2::getKexAlgorithims()
201
+ * @var Array
202
+ * @access private
203
+ */
204
+ var $kex_algorithms;
205
+
206
+ /**
207
+ * Server Host Key Algorithms
208
+ *
209
+ * @see Net_SSH2::getServerHostKeyAlgorithms()
210
+ * @var Array
211
+ * @access private
212
+ */
213
+ var $server_host_key_algorithms;
214
+
215
+ /**
216
+ * Encryption Algorithms: Client to Server
217
+ *
218
+ * @see Net_SSH2::getEncryptionAlgorithmsClient2Server()
219
+ * @var Array
220
+ * @access private
221
+ */
222
+ var $encryption_algorithms_client_to_server;
223
+
224
+ /**
225
+ * Encryption Algorithms: Server to Client
226
+ *
227
+ * @see Net_SSH2::getEncryptionAlgorithmsServer2Client()
228
+ * @var Array
229
+ * @access private
230
+ */
231
+ var $encryption_algorithms_server_to_client;
232
+
233
+ /**
234
+ * MAC Algorithms: Client to Server
235
+ *
236
+ * @see Net_SSH2::getMACAlgorithmsClient2Server()
237
+ * @var Array
238
+ * @access private
239
+ */
240
+ var $mac_algorithms_client_to_server;
241
+
242
+ /**
243
+ * MAC Algorithms: Server to Client
244
+ *
245
+ * @see Net_SSH2::getMACAlgorithmsServer2Client()
246
+ * @var Array
247
+ * @access private
248
+ */
249
+ var $mac_algorithms_server_to_client;
250
+
251
+ /**
252
+ * Compression Algorithms: Client to Server
253
+ *
254
+ * @see Net_SSH2::getCompressionAlgorithmsClient2Server()
255
+ * @var Array
256
+ * @access private
257
+ */
258
+ var $compression_algorithms_client_to_server;
259
+
260
+ /**
261
+ * Compression Algorithms: Server to Client
262
+ *
263
+ * @see Net_SSH2::getCompressionAlgorithmsServer2Client()
264
+ * @var Array
265
+ * @access private
266
+ */
267
+ var $compression_algorithms_server_to_client;
268
+
269
+ /**
270
+ * Languages: Server to Client
271
+ *
272
+ * @see Net_SSH2::getLanguagesServer2Client()
273
+ * @var Array
274
+ * @access private
275
+ */
276
+ var $languages_server_to_client;
277
+
278
+ /**
279
+ * Languages: Client to Server
280
+ *
281
+ * @see Net_SSH2::getLanguagesClient2Server()
282
+ * @var Array
283
+ * @access private
284
+ */
285
+ var $languages_client_to_server;
286
+
287
+ /**
288
+ * Block Size for Server to Client Encryption
289
+ *
290
+ * "Note that the length of the concatenation of 'packet_length',
291
+ * 'padding_length', 'payload', and 'random padding' MUST be a multiple
292
+ * of the cipher block size or 8, whichever is larger. This constraint
293
+ * MUST be enforced, even when using stream ciphers."
294
+ *
295
+ * -- http://tools.ietf.org/html/rfc4253#section-6
296
+ *
297
+ * @see Net_SSH2::Net_SSH2()
298
+ * @see Net_SSH2::_send_binary_packet()
299
+ * @var Integer
300
+ * @access private
301
+ */
302
+ var $encrypt_block_size = 8;
303
+
304
+ /**
305
+ * Block Size for Client to Server Encryption
306
+ *
307
+ * @see Net_SSH2::Net_SSH2()
308
+ * @see Net_SSH2::_get_binary_packet()
309
+ * @var Integer
310
+ * @access private
311
+ */
312
+ var $decrypt_block_size = 8;
313
+
314
+ /**
315
+ * Server to Client Encryption Object
316
+ *
317
+ * @see Net_SSH2::_get_binary_packet()
318
+ * @var Object
319
+ * @access private
320
+ */
321
+ var $decrypt = false;
322
+
323
+ /**
324
+ * Client to Server Encryption Object
325
+ *
326
+ * @see Net_SSH2::_send_binary_packet()
327
+ * @var Object
328
+ * @access private
329
+ */
330
+ var $encrypt = false;
331
+
332
+ /**
333
+ * Client to Server HMAC Object
334
+ *
335
+ * @see Net_SSH2::_send_binary_packet()
336
+ * @var Object
337
+ * @access private
338
+ */
339
+ var $hmac_create = false;
340
+
341
+ /**
342
+ * Server to Client HMAC Object
343
+ *
344
+ * @see Net_SSH2::_get_binary_packet()
345
+ * @var Object
346
+ * @access private
347
+ */
348
+ var $hmac_check = false;
349
+
350
+ /**
351
+ * Size of server to client HMAC
352
+ *
353
+ * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
354
+ * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is
355
+ * append it.
356
+ *
357
+ * @see Net_SSH2::_get_binary_packet()
358
+ * @var Integer
359
+ * @access private
360
+ */
361
+ var $hmac_size = false;
362
+
363
+ /**
364
+ * Server Public Host Key
365
+ *
366
+ * @see Net_SSH2::getServerPublicHostKey()
367
+ * @var String
368
+ * @access private
369
+ */
370
+ var $server_public_host_key;
371
+
372
+ /**
373
+ * Session identifer
374
+ *
375
+ * "The exchange hash H from the first key exchange is additionally
376
+ * used as the session identifier, which is a unique identifier for
377
+ * this connection."
378
+ *
379
+ * -- http://tools.ietf.org/html/rfc4253#section-7.2
380
+ *
381
+ * @see Net_SSH2::_key_exchange()
382
+ * @var String
383
+ * @access private
384
+ */
385
+ var $session_id = false;
386
+
387
+ /**
388
+ * Message Numbers
389
+ *
390
+ * @see Net_SSH2::Net_SSH2()
391
+ * @var Array
392
+ * @access private
393
+ */
394
+ var $message_numbers = array();
395
+
396
+ /**
397
+ * Disconnection Message 'reason codes' defined in RFC4253
398
+ *
399
+ * @see Net_SSH2::Net_SSH2()
400
+ * @var Array
401
+ * @access private
402
+ */
403
+ var $disconnect_reasons = array();
404
+
405
+ /**
406
+ * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
407
+ *
408
+ * @see Net_SSH2::Net_SSH2()
409
+ * @var Array
410
+ * @access private
411
+ */
412
+ var $channel_open_failure_reasons = array();
413
+
414
+ /**
415
+ * Terminal Modes
416
+ *
417
+ * @link http://tools.ietf.org/html/rfc4254#section-8
418
+ * @see Net_SSH2::Net_SSH2()
419
+ * @var Array
420
+ * @access private
421
+ */
422
+ var $terminal_modes = array();
423
+
424
+ /**
425
+ * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
426
+ *
427
+ * @link http://tools.ietf.org/html/rfc4254#section-5.2
428
+ * @see Net_SSH2::Net_SSH2()
429
+ * @var Array
430
+ * @access private
431
+ */
432
+ var $channel_extended_data_type_codes = array();
433
+
434
+ /**
435
+ * Send Sequence Number
436
+ *
437
+ * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
438
+ *
439
+ * @see Net_SSH2::_send_binary_packet()
440
+ * @var Integer
441
+ * @access private
442
+ */
443
+ var $send_seq_no = 0;
444
+
445
+ /**
446
+ * Get Sequence Number
447
+ *
448
+ * See 'Section 6.4. Data Integrity' of rfc4253 for more info.
449
+ *
450
+ * @see Net_SSH2::_get_binary_packet()
451
+ * @var Integer
452
+ * @access private
453
+ */
454
+ var $get_seq_no = 0;
455
+
456
+ /**
457
+ * Server Channels
458
+ *
459
+ * Maps client channels to server channels
460
+ *
461
+ * @see Net_SSH2::_get_channel_packet()
462
+ * @see Net_SSH2::exec()
463
+ * @var Array
464
+ * @access private
465
+ */
466
+ var $server_channels = array();
467
+
468
+ /**
469
+ * Channel Buffers
470
+ *
471
+ * If a client requests a packet from one channel but receives two packets from another those packets should
472
+ * be placed in a buffer
473
+ *
474
+ * @see Net_SSH2::_get_channel_packet()
475
+ * @see Net_SSH2::exec()
476
+ * @var Array
477
+ * @access private
478
+ */
479
+ var $channel_buffers = array();
480
+
481
+ /**
482
+ * Channel Status
483
+ *
484
+ * Contains the type of the last sent message
485
+ *
486
+ * @see Net_SSH2::_get_channel_packet()
487
+ * @var Array
488
+ * @access private
489
+ */
490
+ var $channel_status = array();
491
+
492
+ /**
493
+ * Packet Size
494
+ *
495
+ * Maximum packet size indexed by channel
496
+ *
497
+ * @see Net_SSH2::_send_channel_packet()
498
+ * @var Array
499
+ * @access private
500
+ */
501
+ var $packet_size_client_to_server = array();
502
+
503
+ /**
504
+ * Message Number Log
505
+ *
506
+ * @see Net_SSH2::getLog()
507
+ * @var Array
508
+ * @access private
509
+ */
510
+ var $message_number_log = array();
511
+
512
+ /**
513
+ * Message Log
514
+ *
515
+ * @see Net_SSH2::getLog()
516
+ * @var Array
517
+ * @access private
518
+ */
519
+ var $message_log = array();
520
+
521
+ /**
522
+ * The Window Size
523
+ *
524
+ * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 4GB)
525
+ *
526
+ * @var Integer
527
+ * @see Net_SSH2::_send_channel_packet()
528
+ * @see Net_SSH2::exec()
529
+ * @access private
530
+ */
531
+ var $window_size = 0x7FFFFFFF;
532
+
533
+ /**
534
+ * Window size
535
+ *
536
+ * Window size indexed by channel
537
+ *
538
+ * @see Net_SSH2::_send_channel_packet()
539
+ * @var Array
540
+ * @access private
541
+ */
542
+ var $window_size_client_to_server = array();
543
+
544
+ /**
545
+ * Server signature
546
+ *
547
+ * Verified against $this->session_id
548
+ *
549
+ * @see Net_SSH2::getServerPublicHostKey()
550
+ * @var String
551
+ * @access private
552
+ */
553
+ var $signature = '';
554
+
555
+ /**
556
+ * Server signature format
557
+ *
558
+ * ssh-rsa or ssh-dss.
559
+ *
560
+ * @see Net_SSH2::getServerPublicHostKey()
561
+ * @var String
562
+ * @access private
563
+ */
564
+ var $signature_format = '';
565
+
566
+ /**
567
+ * Default Constructor.
568
+ *
569
+ * Connects to an SSHv2 server
570
+ *
571
+ * @param String $host
572
+ * @param optional Integer $port
573
+ * @param optional Integer $timeout
574
+ * @return Net_SSH2
575
+ * @access public
576
+ */
577
+ function Net_SSH2($host, $port = 22, $timeout = 10)
578
+ {
579
+ $this->message_numbers = array(
580
+ 1 => 'NET_SSH2_MSG_DISCONNECT',
581
+ 2 => 'NET_SSH2_MSG_IGNORE',
582
+ 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
583
+ 4 => 'NET_SSH2_MSG_DEBUG',
584
+ 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
585
+ 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
586
+ 20 => 'NET_SSH2_MSG_KEXINIT',
587
+ 21 => 'NET_SSH2_MSG_NEWKEYS',
588
+ 30 => 'NET_SSH2_MSG_KEXDH_INIT',
589
+ 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
590
+ 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
591
+ 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
592
+ 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
593
+ 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
594
+
595
+ 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
596
+ 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
597
+ 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
598
+ 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
599
+ 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
600
+ 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
601
+ 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
602
+ 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
603
+ 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
604
+ 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
605
+ 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
606
+ 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
607
+ 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
608
+ 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
609
+ );
610
+ $this->disconnect_reasons = array(
611
+ 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
612
+ 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
613
+ 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
614
+ 4 => 'NET_SSH2_DISCONNECT_RESERVED',
615
+ 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
616
+ 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
617
+ 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
618
+ 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
619
+ 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
620
+ 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
621
+ 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
622
+ 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
623
+ 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
624
+ 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
625
+ 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
626
+ );
627
+ $this->channel_open_failure_reasons = array(
628
+ 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
629
+ );
630
+ $this->terminal_modes = array(
631
+ 0 => 'NET_SSH2_TTY_OP_END'
632
+ );
633
+ $this->channel_extended_data_type_codes = array(
634
+ 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
635
+ );
636
+
637
+ $this->_define_array(
638
+ $this->message_numbers,
639
+ $this->disconnect_reasons,
640
+ $this->channel_open_failure_reasons,
641
+ $this->terminal_modes,
642
+ $this->channel_extended_data_type_codes,
643
+ array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
644
+ array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK')
645
+ );
646
+
647
+ $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
648
+ if (!$this->fsock) {
649
+ user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
650
+ return;
651
+ }
652
+
653
+ /* According to the SSH2 specs,
654
+
655
+ "The server MAY send other lines of data before sending the version
656
+ string. Each line SHOULD be terminated by a Carriage Return and Line
657
+ Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
658
+ in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
659
+ MUST be able to process such lines." */
660
+ $temp = '';
661
+ $extra = '';
662
+ while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
663
+ if (substr($temp, -2) == "\r\n") {
664
+ $extra.= $temp;
665
+ $temp = '';
666
+ }
667
+ $temp.= fgets($this->fsock, 255);
668
+ }
669
+
670
+ $ext = array();
671
+ if (extension_loaded('mcrypt')) {
672
+ $ext[] = 'mcrypt';
673
+ }
674
+ if (extension_loaded('gmp')) {
675
+ $ext[] = 'gmp';
676
+ } else if (extension_loaded('bcmath')) {
677
+ $ext[] = 'bcmath';
678
+ }
679
+
680
+ if (!empty($ext)) {
681
+ $this->identifier.= ' (' . implode(', ', $ext) . ')';
682
+ }
683
+
684
+ if (defined('NET_SSH2_LOGGING')) {
685
+ $this->message_number_log[] = '<-';
686
+ $this->message_number_log[] = '->';
687
+
688
+ if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
689
+ $this->message_log[] = $temp;
690
+ $this->message_log[] = $this->identifier . "\r\n";
691
+ }
692
+ }
693
+
694
+ $this->server_identifier = trim($temp, "\r\n");
695
+ if (!empty($extra)) {
696
+ $this->errors[] = utf8_decode($extra);
697
+ }
698
+
699
+ if ($matches[1] != '1.99' && $matches[1] != '2.0') {
700
+ user_error("Cannot connect to SSH $matches[1] servers", E_USER_NOTICE);
701
+ return;
702
+ }
703
+
704
+ fputs($this->fsock, $this->identifier . "\r\n");
705
+
706
+ $response = $this->_get_binary_packet();
707
+ if ($response === false) {
708
+ user_error('Connection closed by server', E_USER_NOTICE);
709
+ return;
710
+ }
711
+
712
+ if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
713
+ user_error('Expected SSH_MSG_KEXINIT', E_USER_NOTICE);
714
+ return;
715
+ }
716
+
717
+ if (!$this->_key_exchange($response)) {
718
+ return;
719
+ }
720
+
721
+ $this->bitmap = NET_SSH2_MASK_CONSTRUCTOR;
722
+ }
723
+
724
+ /**
725
+ * Key Exchange
726
+ *
727
+ * @param String $kexinit_payload_server
728
+ * @access private
729
+ */
730
+ function _key_exchange($kexinit_payload_server)
731
+ {
732
+ static $kex_algorithms = array(
733
+ 'diffie-hellman-group1-sha1', // REQUIRED
734
+ 'diffie-hellman-group14-sha1' // REQUIRED
735
+ );
736
+
737
+ static $server_host_key_algorithms = array(
738
+ 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
739
+ 'ssh-dss' // REQUIRED sign Raw DSS Key
740
+ );
741
+
742
+ static $encryption_algorithms = array(
743
+ // from <http://tools.ietf.org/html/rfc4345#section-4>:
744
+ 'arcfour256',
745
+ 'arcfour128',
746
+
747
+ 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
748
+
749
+ 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
750
+ 'aes192-cbc', // OPTIONAL AES with a 192-bit key
751
+ 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
752
+
753
+ // from <http://tools.ietf.org/html/rfc4344#section-4>:
754
+ 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
755
+ 'aes192-ctr', // RECOMMENDED AES with 192-bit key
756
+ 'aes256-ctr', // RECOMMENDED AES with 256-bit key
757
+ '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
758
+
759
+ '3des-cbc', // REQUIRED three-key 3DES in CBC mode
760
+ 'none' // OPTIONAL no encryption; NOT RECOMMENDED
761
+ );
762
+
763
+ static $mac_algorithms = array(
764
+ 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
765
+ 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
766
+ 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
767
+ 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
768
+ 'none' // OPTIONAL no MAC; NOT RECOMMENDED
769
+ );
770
+
771
+ static $compression_algorithms = array(
772
+ 'none' // REQUIRED no compression
773
+ //'zlib' // OPTIONAL ZLIB (LZ77) compression
774
+ );
775
+
776
+ static $str_kex_algorithms, $str_server_host_key_algorithms,
777
+ $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
778
+ $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
779
+
780
+ if (empty($str_kex_algorithms)) {
781
+ $str_kex_algorithms = implode(',', $kex_algorithms);
782
+ $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
783
+ $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
784
+ $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
785
+ $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
786
+ }
787
+
788
+ $client_cookie = '';
789
+ for ($i = 0; $i < 16; $i++) {
790
+ $client_cookie.= chr(crypt_random(0, 255));
791
+ }
792
+
793
+ $response = $kexinit_payload_server;
794
+ $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
795
+ $server_cookie = $this->_string_shift($response, 16);
796
+
797
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
798
+ $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
799
+
800
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
801
+ $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
802
+
803
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
804
+ $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
805
+
806
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
807
+ $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
808
+
809
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
810
+ $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
811
+
812
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
813
+ $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
814
+
815
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
816
+ $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
817
+
818
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
819
+ $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
820
+
821
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
822
+ $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
823
+
824
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
825
+ $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
826
+
827
+ extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
828
+ $first_kex_packet_follows = $first_kex_packet_follows != 0;
829
+
830
+ // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
831
+ $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
832
+ NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms,
833
+ strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server),
834
+ $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client,
835
+ strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client),
836
+ $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server,
837
+ strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '',
838
+ 0, 0
839
+ );
840
+
841
+ if (!$this->_send_binary_packet($kexinit_payload_client)) {
842
+ return false;
843
+ }
844
+ // here ends the second place.
845
+
846
+ // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
847
+ for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++);
848
+ if ($i == count($encryption_algorithms)) {
849
+ user_error('No compatible server to client encryption algorithms found', E_USER_NOTICE);
850
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
851
+ }
852
+
853
+ // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
854
+ // diffie-hellman key exchange as fast as possible
855
+ $decrypt = $encryption_algorithms[$i];
856
+ switch ($decrypt) {
857
+ case '3des-cbc':
858
+ case '3des-ctr':
859
+ $decryptKeyLength = 24; // eg. 192 / 8
860
+ break;
861
+ case 'aes256-cbc':
862
+ case 'aes256-ctr':
863
+ $decryptKeyLength = 32; // eg. 256 / 8
864
+ break;
865
+ case 'aes192-cbc':
866
+ case 'aes192-ctr':
867
+ $decryptKeyLength = 24; // eg. 192 / 8
868
+ break;
869
+ case 'aes128-cbc':
870
+ case 'aes128-ctr':
871
+ $decryptKeyLength = 16; // eg. 128 / 8
872
+ break;
873
+ case 'arcfour':
874
+ case 'arcfour128':
875
+ $decryptKeyLength = 16; // eg. 128 / 8
876
+ break;
877
+ case 'arcfour256':
878
+ $decryptKeyLength = 32; // eg. 128 / 8
879
+ break;
880
+ case 'none';
881
+ $decryptKeyLength = 0;
882
+ }
883
+
884
+ for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++);
885
+ if ($i == count($encryption_algorithms)) {
886
+ user_error('No compatible client to server encryption algorithms found', E_USER_NOTICE);
887
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
888
+ }
889
+
890
+ $encrypt = $encryption_algorithms[$i];
891
+ switch ($encrypt) {
892
+ case '3des-cbc':
893
+ case '3des-ctr':
894
+ $encryptKeyLength = 24;
895
+ break;
896
+ case 'aes256-cbc':
897
+ case 'aes256-ctr':
898
+ $encryptKeyLength = 32;
899
+ break;
900
+ case 'aes192-cbc':
901
+ case 'aes192-ctr':
902
+ $encryptKeyLength = 24;
903
+ break;
904
+ case 'aes128-cbc':
905
+ case 'aes128-ctr':
906
+ $encryptKeyLength = 16;
907
+ break;
908
+ case 'arcfour':
909
+ case 'arcfour128':
910
+ $encryptKeyLength = 16;
911
+ break;
912
+ case 'arcfour256':
913
+ $encryptKeyLength = 32;
914
+ break;
915
+ case 'none';
916
+ $encryptKeyLength = 0;
917
+ }
918
+
919
+ $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
920
+
921
+ // through diffie-hellman key exchange a symmetric key is obtained
922
+ for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++);
923
+ if ($i == count($kex_algorithms)) {
924
+ user_error('No compatible key exchange algorithms found', E_USER_NOTICE);
925
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
926
+ }
927
+
928
+ switch ($kex_algorithms[$i]) {
929
+ // see http://tools.ietf.org/html/rfc2409#section-6.2 and
930
+ // http://tools.ietf.org/html/rfc2412, appendex E
931
+ case 'diffie-hellman-group1-sha1':
932
+ $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
933
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
934
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
935
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF');
936
+ $keyLength = $keyLength < 160 ? $keyLength : 160;
937
+ $hash = 'sha1';
938
+ break;
939
+ // see http://tools.ietf.org/html/rfc3526#section-3
940
+ case 'diffie-hellman-group14-sha1':
941
+ $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
942
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
943
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
944
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
945
+ '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
946
+ '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
947
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
948
+ '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF');
949
+ $keyLength = $keyLength < 160 ? $keyLength : 160;
950
+ $hash = 'sha1';
951
+ }
952
+
953
+ $p = new Math_BigInteger($p, 256);
954
+ //$q = $p->bitwise_rightShift(1);
955
+
956
+ /* To increase the speed of the key exchange, both client and server may
957
+ reduce the size of their private exponents. It should be at least
958
+ twice as long as the key material that is generated from the shared
959
+ secret. For more details, see the paper by van Oorschot and Wiener
960
+ [VAN-OORSCHOT].
961
+
962
+ -- http://tools.ietf.org/html/rfc4419#section-6.2 */
963
+ $q = new Math_BigInteger(1);
964
+ $q = $q->bitwise_leftShift(2 * $keyLength);
965
+ $q = $q->subtract(new Math_BigInteger(1));
966
+
967
+ $g = new Math_BigInteger(2);
968
+ $x = new Math_BigInteger();
969
+ $x->setRandomGenerator('crypt_random');
970
+ $x = $x->random(new Math_BigInteger(1), $q);
971
+ $e = $g->modPow($x, $p);
972
+
973
+ $eBytes = $e->toBytes(true);
974
+ $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
975
+
976
+ if (!$this->_send_binary_packet($data)) {
977
+ user_error('Connection closed by server', E_USER_NOTICE);
978
+ return false;
979
+ }
980
+
981
+ $response = $this->_get_binary_packet();
982
+ if ($response === false) {
983
+ user_error('Connection closed by server', E_USER_NOTICE);
984
+ return false;
985
+ }
986
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
987
+
988
+ if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
989
+ user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE);
990
+ return false;
991
+ }
992
+
993
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
994
+ $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
995
+
996
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
997
+ $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
998
+
999
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
1000
+ $fBytes = $this->_string_shift($response, $temp['length']);
1001
+ $f = new Math_BigInteger($fBytes, -256);
1002
+
1003
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
1004
+ $this->signature = $this->_string_shift($response, $temp['length']);
1005
+
1006
+ $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
1007
+ $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
1008
+
1009
+ $key = $f->modPow($x, $p);
1010
+ $keyBytes = $key->toBytes(true);
1011
+
1012
+ if ($this->session_id === false) {
1013
+ $source = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
1014
+ strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
1015
+ strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
1016
+ $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
1017
+ $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
1018
+ );
1019
+
1020
+ $source = pack('H*', $hash($source));
1021
+
1022
+ $this->session_id = $source;
1023
+ }
1024
+
1025
+ for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
1026
+ if ($i == count($server_host_key_algorithms)) {
1027
+ user_error('No compatible server host key algorithms found', E_USER_NOTICE);
1028
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1029
+ }
1030
+
1031
+ if ($public_key_format != $server_host_key_algorithms[$i] || $this->signature_format != $server_host_key_algorithms[$i]) {
1032
+ user_error('Sever Host Key Algorithm Mismatch', E_USER_NOTICE);
1033
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1034
+ }
1035
+
1036
+ $packet = pack('C',
1037
+ NET_SSH2_MSG_NEWKEYS
1038
+ );
1039
+
1040
+ if (!$this->_send_binary_packet($packet)) {
1041
+ return false;
1042
+ }
1043
+
1044
+ $response = $this->_get_binary_packet();
1045
+
1046
+ if ($response === false) {
1047
+ user_error('Connection closed by server', E_USER_NOTICE);
1048
+ return false;
1049
+ }
1050
+
1051
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1052
+
1053
+ if ($type != NET_SSH2_MSG_NEWKEYS) {
1054
+ user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE);
1055
+ return false;
1056
+ }
1057
+
1058
+ switch ($encrypt) {
1059
+ case '3des-cbc':
1060
+ $this->encrypt = new Crypt_TripleDES();
1061
+ // $this->encrypt_block_size = 64 / 8 == the default
1062
+ break;
1063
+ case '3des-ctr':
1064
+ $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
1065
+ // $this->encrypt_block_size = 64 / 8 == the default
1066
+ break;
1067
+ case 'aes256-cbc':
1068
+ case 'aes192-cbc':
1069
+ case 'aes128-cbc':
1070
+ $this->encrypt = new Crypt_AES();
1071
+ $this->encrypt_block_size = 16; // eg. 128 / 8
1072
+ break;
1073
+ case 'aes256-ctr':
1074
+ case 'aes192-ctr':
1075
+ case 'aes128-ctr':
1076
+ $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
1077
+ $this->encrypt_block_size = 16; // eg. 128 / 8
1078
+ break;
1079
+ case 'arcfour':
1080
+ case 'arcfour128':
1081
+ case 'arcfour256':
1082
+ $this->encrypt = new Crypt_RC4();
1083
+ break;
1084
+ case 'none';
1085
+ //$this->encrypt = new Crypt_Null();
1086
+ }
1087
+
1088
+ switch ($decrypt) {
1089
+ case '3des-cbc':
1090
+ $this->decrypt = new Crypt_TripleDES();
1091
+ break;
1092
+ case '3des-ctr':
1093
+ $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
1094
+ break;
1095
+ case 'aes256-cbc':
1096
+ case 'aes192-cbc':
1097
+ case 'aes128-cbc':
1098
+ $this->decrypt = new Crypt_AES();
1099
+ $this->decrypt_block_size = 16;
1100
+ break;
1101
+ case 'aes256-ctr':
1102
+ case 'aes192-ctr':
1103
+ case 'aes128-ctr':
1104
+ $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR);
1105
+ $this->decrypt_block_size = 16;
1106
+ break;
1107
+ case 'arcfour':
1108
+ case 'arcfour128':
1109
+ case 'arcfour256':
1110
+ $this->decrypt = new Crypt_RC4();
1111
+ break;
1112
+ case 'none';
1113
+ //$this->decrypt = new Crypt_Null();
1114
+ }
1115
+
1116
+ $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
1117
+
1118
+ if ($this->encrypt) {
1119
+ $this->encrypt->enableContinuousBuffer();
1120
+ $this->encrypt->disablePadding();
1121
+
1122
+ $iv = pack('H*', $hash($keyBytes . $this->session_id . 'A' . $this->session_id));
1123
+ while ($this->encrypt_block_size > strlen($iv)) {
1124
+ $iv.= pack('H*', $hash($keyBytes . $this->session_id . $iv));
1125
+ }
1126
+ $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
1127
+
1128
+ $key = pack('H*', $hash($keyBytes . $this->session_id . 'C' . $this->session_id));
1129
+ while ($encryptKeyLength > strlen($key)) {
1130
+ $key.= pack('H*', $hash($keyBytes . $this->session_id . $key));
1131
+ }
1132
+ $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
1133
+ }
1134
+
1135
+ if ($this->decrypt) {
1136
+ $this->decrypt->enableContinuousBuffer();
1137
+ $this->decrypt->disablePadding();
1138
+
1139
+ $iv = pack('H*', $hash($keyBytes . $this->session_id . 'B' . $this->session_id));
1140
+ while ($this->decrypt_block_size > strlen($iv)) {
1141
+ $iv.= pack('H*', $hash($keyBytes . $this->session_id . $iv));
1142
+ }
1143
+ $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
1144
+
1145
+ $key = pack('H*', $hash($keyBytes . $this->session_id . 'D' . $this->session_id));
1146
+ while ($decryptKeyLength > strlen($key)) {
1147
+ $key.= pack('H*', $hash($keyBytes . $this->session_id . $key));
1148
+ }
1149
+ $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
1150
+ }
1151
+
1152
+ /* The "arcfour128" algorithm is the RC4 cipher, as described in
1153
+ [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
1154
+ generated by the cipher MUST be discarded, and the first byte of the
1155
+ first encrypted packet MUST be encrypted using the 1537th byte of
1156
+ keystream.
1157
+
1158
+ -- http://tools.ietf.org/html/rfc4345#section-4 */
1159
+ if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
1160
+ $this->encrypt->encrypt(str_repeat("\0", 1536));
1161
+ }
1162
+ if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
1163
+ $this->decrypt->decrypt(str_repeat("\0", 1536));
1164
+ }
1165
+
1166
+ for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++);
1167
+ if ($i == count($mac_algorithms)) {
1168
+ user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);
1169
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1170
+ }
1171
+
1172
+ $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none'
1173
+ switch ($mac_algorithms[$i]) {
1174
+ case 'hmac-sha1':
1175
+ $this->hmac_create = new Crypt_Hash('sha1');
1176
+ $createKeyLength = 20;
1177
+ break;
1178
+ case 'hmac-sha1-96':
1179
+ $this->hmac_create = new Crypt_Hash('sha1-96');
1180
+ $createKeyLength = 20;
1181
+ break;
1182
+ case 'hmac-md5':
1183
+ $this->hmac_create = new Crypt_Hash('md5');
1184
+ $createKeyLength = 16;
1185
+ break;
1186
+ case 'hmac-md5-96':
1187
+ $this->hmac_create = new Crypt_Hash('md5-96');
1188
+ $createKeyLength = 16;
1189
+ }
1190
+
1191
+ for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++);
1192
+ if ($i == count($mac_algorithms)) {
1193
+ user_error('No compatible server to client message authentication algorithms found', E_USER_NOTICE);
1194
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1195
+ }
1196
+
1197
+ $checkKeyLength = 0;
1198
+ $this->hmac_size = 0;
1199
+ switch ($mac_algorithms[$i]) {
1200
+ case 'hmac-sha1':
1201
+ $this->hmac_check = new Crypt_Hash('sha1');
1202
+ $checkKeyLength = 20;
1203
+ $this->hmac_size = 20;
1204
+ break;
1205
+ case 'hmac-sha1-96':
1206
+ $this->hmac_check = new Crypt_Hash('sha1-96');
1207
+ $checkKeyLength = 20;
1208
+ $this->hmac_size = 12;
1209
+ break;
1210
+ case 'hmac-md5':
1211
+ $this->hmac_check = new Crypt_Hash('md5');
1212
+ $checkKeyLength = 16;
1213
+ $this->hmac_size = 16;
1214
+ break;
1215
+ case 'hmac-md5-96':
1216
+ $this->hmac_check = new Crypt_Hash('md5-96');
1217
+ $checkKeyLength = 16;
1218
+ $this->hmac_size = 12;
1219
+ }
1220
+
1221
+ $key = pack('H*', $hash($keyBytes . $this->session_id . 'E' . $this->session_id));
1222
+ while ($createKeyLength > strlen($key)) {
1223
+ $key.= pack('H*', $hash($keyBytes . $this->session_id . $key));
1224
+ }
1225
+ $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
1226
+
1227
+ $key = pack('H*', $hash($keyBytes . $this->session_id . 'F' . $this->session_id));
1228
+ while ($checkKeyLength > strlen($key)) {
1229
+ $key.= pack('H*', $hash($keyBytes . $this->session_id . $key));
1230
+ }
1231
+ $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
1232
+
1233
+ for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++);
1234
+ if ($i == count($compression_algorithms)) {
1235
+ user_error('No compatible server to client compression algorithms found', E_USER_NOTICE);
1236
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1237
+ }
1238
+ $this->decompress = $compression_algorithms[$i] == 'zlib';
1239
+
1240
+ for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++);
1241
+ if ($i == count($compression_algorithms)) {
1242
+ user_error('No compatible client to server compression algorithms found', E_USER_NOTICE);
1243
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1244
+ }
1245
+ $this->compress = $compression_algorithms[$i] == 'zlib';
1246
+
1247
+ return true;
1248
+ }
1249
+
1250
+ /**
1251
+ * Login
1252
+ *
1253
+ * @param String $username
1254
+ * @param optional String $password
1255
+ * @return Boolean
1256
+ * @access public
1257
+ * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
1258
+ * by sending dummy SSH_MSG_IGNORE messages.
1259
+ */
1260
+ function login($username, $password = '')
1261
+ {
1262
+ if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
1263
+ return false;
1264
+ }
1265
+
1266
+ $packet = pack('CNa*',
1267
+ NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
1268
+ );
1269
+
1270
+ if (!$this->_send_binary_packet($packet)) {
1271
+ return false;
1272
+ }
1273
+
1274
+ $response = $this->_get_binary_packet();
1275
+ if ($response === false) {
1276
+ user_error('Connection closed by server', E_USER_NOTICE);
1277
+ return false;
1278
+ }
1279
+
1280
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1281
+
1282
+ if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
1283
+ user_error('Expected SSH_MSG_SERVICE_ACCEPT', E_USER_NOTICE);
1284
+ return false;
1285
+ }
1286
+
1287
+ // although PHP5's get_class() preserves the case, PHP4's does not
1288
+ if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') {
1289
+ return $this->_privatekey_login($username, $password);
1290
+ }
1291
+
1292
+ $utf8_password = utf8_encode($password);
1293
+ $packet = pack('CNa*Na*Na*CNa*',
1294
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
1295
+ strlen('password'), 'password', 0, strlen($utf8_password), $utf8_password
1296
+ );
1297
+
1298
+ if (!$this->_send_binary_packet($packet)) {
1299
+ return false;
1300
+ }
1301
+
1302
+ // remove the username and password from the last logged packet
1303
+ if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
1304
+ $packet = pack('CNa*Na*Na*CNa*',
1305
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection',
1306
+ strlen('password'), 'password', 0, strlen('password'), 'password'
1307
+ );
1308
+ $this->message_log[count($this->message_log) - 1] = $packet;
1309
+ }
1310
+
1311
+ $response = $this->_get_binary_packet();
1312
+ if ($response === false) {
1313
+ user_error('Connection closed by server', E_USER_NOTICE);
1314
+ return false;
1315
+ }
1316
+
1317
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1318
+
1319
+ switch ($type) {
1320
+ case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
1321
+ if (defined('NET_SSH2_LOGGING')) {
1322
+ $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
1323
+ }
1324
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1325
+ $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
1326
+ return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
1327
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
1328
+ // either the login is bad or the server employees multi-factor authentication
1329
+ return false;
1330
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
1331
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
1332
+ return true;
1333
+ }
1334
+
1335
+ return false;
1336
+ }
1337
+
1338
+ /**
1339
+ * Login with an RSA private key
1340
+ *
1341
+ * @param String $username
1342
+ * @param Crypt_RSA $password
1343
+ * @return Boolean
1344
+ * @access private
1345
+ * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
1346
+ * by sending dummy SSH_MSG_IGNORE messages.
1347
+ */
1348
+ function _privatekey_login($username, $privatekey)
1349
+ {
1350
+ // see http://tools.ietf.org/html/rfc4253#page-15
1351
+ $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
1352
+ if ($publickey === false) {
1353
+ return false;
1354
+ }
1355
+
1356
+ $publickey = array(
1357
+ 'e' => $publickey['e']->toBytes(true),
1358
+ 'n' => $publickey['n']->toBytes(true)
1359
+ );
1360
+ $publickey = pack('Na*Na*Na*',
1361
+ strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n']
1362
+ );
1363
+
1364
+ $part1 = pack('CNa*Na*Na*',
1365
+ NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
1366
+ strlen('publickey'), 'publickey'
1367
+ );
1368
+ $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
1369
+
1370
+ $packet = $part1 . chr(0) . $part2;
1371
+
1372
+ if (!$this->_send_binary_packet($packet)) {
1373
+ return false;
1374
+ }
1375
+
1376
+ $response = $this->_get_binary_packet();
1377
+ if ($response === false) {
1378
+ user_error('Connection closed by server', E_USER_NOTICE);
1379
+ return false;
1380
+ }
1381
+
1382
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1383
+
1384
+ switch ($type) {
1385
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
1386
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1387
+ $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
1388
+ return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
1389
+ case NET_SSH2_MSG_USERAUTH_PK_OK:
1390
+ // we'll just take it on faith that the public key blob and the public key algorithm name are as
1391
+ // they should be
1392
+ if (defined('NET_SSH2_LOGGING')) {
1393
+ $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PK_OK';
1394
+ }
1395
+ }
1396
+
1397
+ $packet = $part1 . chr(1) . $part2;
1398
+ $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
1399
+ $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
1400
+ $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
1401
+ $packet.= pack('Na*', strlen($signature), $signature);
1402
+
1403
+ if (!$this->_send_binary_packet($packet)) {
1404
+ return false;
1405
+ }
1406
+
1407
+ $response = $this->_get_binary_packet();
1408
+ if ($response === false) {
1409
+ user_error('Connection closed by server', E_USER_NOTICE);
1410
+ return false;
1411
+ }
1412
+
1413
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
1414
+
1415
+ switch ($type) {
1416
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
1417
+ // either the login is bad or the server employees multi-factor authentication
1418
+ return false;
1419
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
1420
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
1421
+ return true;
1422
+ }
1423
+
1424
+ return false;
1425
+ }
1426
+
1427
+ /**
1428
+ * Execute Command
1429
+ *
1430
+ * @param String $command
1431
+ * @return String
1432
+ * @access public
1433
+ */
1434
+ function exec($command)
1435
+ {
1436
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1437
+ return false;
1438
+ }
1439
+
1440
+ // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
1441
+ // be adjusted". 0x7FFFFFFF is, at 4GB, the max size. technically, it should probably be decremented, but,
1442
+ // honestly, if you're transfering more than 4GB, you probably shouldn't be using phpseclib, anyway.
1443
+ // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
1444
+ $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC] = 0x7FFFFFFF;
1445
+ // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
1446
+ // uses 0x4000, that's what will be used here, as well.
1447
+ $packet_size = 0x4000;
1448
+
1449
+ $packet = pack('CNa*N3',
1450
+ NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_EXEC, $this->window_size_client_to_server[NET_SSH2_CHANNEL_EXEC], $packet_size);
1451
+
1452
+ if (!$this->_send_binary_packet($packet)) {
1453
+ return false;
1454
+ }
1455
+
1456
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
1457
+
1458
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
1459
+ if ($response === false) {
1460
+ return false;
1461
+ }
1462
+
1463
+ // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
1464
+ // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
1465
+ // with a pty-req SSH_MSG_cHANNEL_REQUEST, exec() will return immediately and the ping process will then
1466
+ // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
1467
+ // neither will your script.
1468
+
1469
+ // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
1470
+ // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
1471
+ // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
1472
+ $packet = pack('CNNa*CNa*',
1473
+ NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command);
1474
+ if (!$this->_send_binary_packet($packet)) {
1475
+ return false;
1476
+ }
1477
+
1478
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
1479
+
1480
+ $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
1481
+ if ($response === false) {
1482
+ return false;
1483
+ }
1484
+
1485
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
1486
+
1487
+ $output = '';
1488
+ while (true) {
1489
+ $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
1490
+ switch (true) {
1491
+ case $temp === true:
1492
+ return $output;
1493
+ case $temp === false:
1494
+ return false;
1495
+ default:
1496
+ $output.= $temp;
1497
+ }
1498
+ }
1499
+ }
1500
+
1501
+ /**
1502
+ * Disconnect
1503
+ *
1504
+ * @access public
1505
+ */
1506
+ function disconnect()
1507
+ {
1508
+ $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
1509
+ }
1510
+
1511
+ /**
1512
+ * Destructor.
1513
+ *
1514
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
1515
+ * disconnect().
1516
+ *
1517
+ * @access public
1518
+ */
1519
+ function __destruct()
1520
+ {
1521
+ $this->disconnect();
1522
+ }
1523
+
1524
+ /**
1525
+ * Gets Binary Packets
1526
+ *
1527
+ * See '6. Binary Packet Protocol' of rfc4253 for more info.
1528
+ *
1529
+ * @see Net_SSH2::_send_binary_packet()
1530
+ * @return String
1531
+ * @access private
1532
+ */
1533
+ function _get_binary_packet()
1534
+ {
1535
+ if (feof($this->fsock)) {
1536
+ user_error('Connection closed prematurely', E_USER_NOTICE);
1537
+ return false;
1538
+ }
1539
+
1540
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1541
+ $raw = fread($this->fsock, $this->decrypt_block_size);
1542
+ $stop = strtok(microtime(), ' ') + strtok('');
1543
+
1544
+ if ($this->decrypt !== false) {
1545
+ $raw = $this->decrypt->decrypt($raw);
1546
+ }
1547
+
1548
+ extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
1549
+
1550
+ $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
1551
+ $buffer = '';
1552
+ while ($remaining_length > 0) {
1553
+ $temp = fread($this->fsock, $remaining_length);
1554
+ $buffer.= $temp;
1555
+ $remaining_length-= strlen($temp);
1556
+ }
1557
+ if (!empty($buffer)) {
1558
+ $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
1559
+ $buffer = $temp = '';
1560
+ }
1561
+
1562
+ $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
1563
+ $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
1564
+
1565
+ if ($this->hmac_check !== false) {
1566
+ $hmac = fread($this->fsock, $this->hmac_size);
1567
+ if ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
1568
+ user_error('Invalid HMAC', E_USER_NOTICE);
1569
+ return false;
1570
+ }
1571
+ }
1572
+
1573
+ //if ($this->decompress) {
1574
+ // $payload = gzinflate(substr($payload, 2));
1575
+ //}
1576
+
1577
+ $this->get_seq_no++;
1578
+
1579
+ if (defined('NET_SSH2_LOGGING')) {
1580
+ $temp = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN';
1581
+ $this->message_number_log[] = '<- ' . $temp .
1582
+ ' (' . round($stop - $start, 4) . 's)';
1583
+ if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
1584
+ $this->message_log[] = substr($payload, 1);
1585
+ }
1586
+ }
1587
+
1588
+ return $this->_filter($payload);
1589
+ }
1590
+
1591
+ /**
1592
+ * Filter Binary Packets
1593
+ *
1594
+ * Because some binary packets need to be ignored...
1595
+ *
1596
+ * @see Net_SSH2::_get_binary_packet()
1597
+ * @return String
1598
+ * @access private
1599
+ */
1600
+ function _filter($payload)
1601
+ {
1602
+ switch (ord($payload[0])) {
1603
+ case NET_SSH2_MSG_DISCONNECT:
1604
+ $this->_string_shift($payload, 1);
1605
+ extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
1606
+ $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
1607
+ $this->bitmask = 0;
1608
+ return false;
1609
+ case NET_SSH2_MSG_IGNORE:
1610
+ $payload = $this->_get_binary_packet();
1611
+ break;
1612
+ case NET_SSH2_MSG_DEBUG:
1613
+ $this->_string_shift($payload, 2);
1614
+ extract(unpack('Nlength', $this->_string_shift($payload, 4)));
1615
+ $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
1616
+ $payload = $this->_get_binary_packet();
1617
+ break;
1618
+ case NET_SSH2_MSG_UNIMPLEMENTED:
1619
+ return false;
1620
+ case NET_SSH2_MSG_KEXINIT:
1621
+ if ($this->session_id !== false) {
1622
+ if (!$this->_key_exchange($payload)) {
1623
+ $this->bitmask = 0;
1624
+ return false;
1625
+ }
1626
+ $payload = $this->_get_binary_packet();
1627
+ }
1628
+ }
1629
+
1630
+ // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
1631
+ if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && !($this->bitmap & NET_SSH2_MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
1632
+ $this->_string_shift($payload, 1);
1633
+ extract(unpack('Nlength', $this->_string_shift($payload, 4)));
1634
+ $this->errors[] = 'SSH_MSG_USERAUTH_BANNER: ' . utf8_decode($this->_string_shift($payload, $length));
1635
+ $payload = $this->_get_binary_packet();
1636
+ }
1637
+
1638
+ // only called when we've already logged in
1639
+ if (($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) && ($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1640
+ switch (ord($payload[0])) {
1641
+ case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
1642
+ $this->_string_shift($payload, 1);
1643
+ extract(unpack('Nlength', $this->_string_shift($payload)));
1644
+ $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . utf8_decode($this->_string_shift($payload, $length));
1645
+
1646
+ if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
1647
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
1648
+ }
1649
+
1650
+ $payload = $this->_get_binary_packet();
1651
+ break;
1652
+ case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
1653
+ $this->_string_shift($payload, 1);
1654
+ extract(unpack('N', $this->_string_shift($payload, 4)));
1655
+ $this->errors[] = 'SSH_MSG_CHANNEL_OPEN: ' . utf8_decode($this->_string_shift($payload, $length));
1656
+
1657
+ $this->_string_shift($payload, 4); // skip over client channel
1658
+ extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
1659
+
1660
+ $packet = pack('CN3a*Na*',
1661
+ NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '');
1662
+
1663
+ if (!$this->_send_binary_packet($packet)) {
1664
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
1665
+ }
1666
+
1667
+ $payload = $this->_get_binary_packet();
1668
+ break;
1669
+ case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
1670
+ $payload = $this->_get_binary_packet();
1671
+ }
1672
+ }
1673
+
1674
+ return $payload;
1675
+ }
1676
+
1677
+ /**
1678
+ * Gets channel data
1679
+ *
1680
+ * Returns the data as a string if it's available and false if not.
1681
+ *
1682
+ * @param $client_channel
1683
+ * @return Mixed
1684
+ * @access private
1685
+ */
1686
+ function _get_channel_packet($client_channel)
1687
+ {
1688
+ if (!empty($this->channel_buffers[$client_channel])) {
1689
+ return array_shift($this->channel_buffers[$client_channel]);
1690
+ }
1691
+
1692
+ while (true) {
1693
+ $response = $this->_get_binary_packet();
1694
+ if ($response === false) {
1695
+ user_error('Connection closed by server', E_USER_NOTICE);
1696
+ return false;
1697
+ }
1698
+
1699
+ extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
1700
+
1701
+ switch ($this->channel_status[$channel]) {
1702
+ case NET_SSH2_MSG_CHANNEL_OPEN:
1703
+ switch ($type) {
1704
+ case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
1705
+ extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
1706
+ $this->server_channels[$client_channel] = $server_channel;
1707
+ $this->_string_shift($response, 4); // skip over (server) window size
1708
+ $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
1709
+ $this->packet_size_client_to_server[$client_channel] = $temp['packet_size_client_to_server'];
1710
+ return true;
1711
+ //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
1712
+ default:
1713
+ user_error('Unable to open channel', E_USER_NOTICE);
1714
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
1715
+ }
1716
+ break;
1717
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
1718
+ switch ($type) {
1719
+ case NET_SSH2_MSG_CHANNEL_SUCCESS:
1720
+ return true;
1721
+ //case NET_SSH2_MSG_CHANNEL_FAILURE:
1722
+ default:
1723
+ user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
1724
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
1725
+ }
1726
+
1727
+ }
1728
+
1729
+ switch ($type) {
1730
+ case NET_SSH2_MSG_CHANNEL_DATA:
1731
+ if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
1732
+ // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
1733
+ // this actually seems to make things twice as fast. more to the point, the message right after
1734
+ // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
1735
+ // in OpenSSH it slows things down but only by a couple thousandths of a second.
1736
+ $this->_send_channel_packet($client_channel, chr(0));
1737
+ }
1738
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1739
+ $data = $this->_string_shift($response, $length);
1740
+ if ($client_channel == $channel) {
1741
+ return $data;
1742
+ }
1743
+ if (!isset($this->channel_buffers[$client_channel])) {
1744
+ $this->channel_buffers[$client_channel] = array();
1745
+ }
1746
+ $this->channel_buffers[$client_channel][] = $data;
1747
+ break;
1748
+ case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
1749
+ if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
1750
+ $this->_send_channel_packet($client_channel, chr(0));
1751
+ }
1752
+ // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
1753
+ extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
1754
+ $data = $this->_string_shift($response, $length);
1755
+ if ($client_channel == $channel) {
1756
+ return $data;
1757
+ }
1758
+ if (!isset($this->channel_buffers[$client_channel])) {
1759
+ $this->channel_buffers[$client_channel] = array();
1760
+ }
1761
+ $this->channel_buffers[$client_channel][] = $data;
1762
+ break;
1763
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
1764
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1765
+ $value = $this->_string_shift($response, $length);
1766
+ switch ($value) {
1767
+ case 'exit-signal':
1768
+ $this->_string_shift($response, 1);
1769
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1770
+ $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
1771
+ $this->_string_shift($response, 1);
1772
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1773
+ if ($length) {
1774
+ $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
1775
+ }
1776
+ //case 'exit-status':
1777
+ default:
1778
+ // "Some systems may not implement signals, in which case they SHOULD ignore this message."
1779
+ // -- http://tools.ietf.org/html/rfc4254#section-6.9
1780
+ break;
1781
+ }
1782
+ break;
1783
+ case NET_SSH2_MSG_CHANNEL_CLOSE:
1784
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
1785
+ return true;
1786
+ case NET_SSH2_MSG_CHANNEL_EOF:
1787
+ break;
1788
+ default:
1789
+ user_error('Error reading channel data', E_USER_NOTICE);
1790
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
1791
+ }
1792
+ }
1793
+ }
1794
+
1795
+ /**
1796
+ * Sends Binary Packets
1797
+ *
1798
+ * See '6. Binary Packet Protocol' of rfc4253 for more info.
1799
+ *
1800
+ * @param String $data
1801
+ * @see Net_SSH2::_get_binary_packet()
1802
+ * @return Boolean
1803
+ * @access private
1804
+ */
1805
+ function _send_binary_packet($data)
1806
+ {
1807
+ if (feof($this->fsock)) {
1808
+ user_error('Connection closed prematurely', E_USER_NOTICE);
1809
+ return false;
1810
+ }
1811
+
1812
+ //if ($this->compress) {
1813
+ // // the -4 removes the checksum:
1814
+ // // http://php.net/function.gzcompress#57710
1815
+ // $data = substr(gzcompress($data), 0, -4);
1816
+ //}
1817
+
1818
+ // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
1819
+ $packet_length = strlen($data) + 9;
1820
+ // round up to the nearest $this->encrypt_block_size
1821
+ $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
1822
+ // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
1823
+ $padding_length = $packet_length - strlen($data) - 5;
1824
+
1825
+ $padding = '';
1826
+ for ($i = 0; $i < $padding_length; $i++) {
1827
+ $padding.= chr(crypt_random(0, 255));
1828
+ }
1829
+
1830
+ // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
1831
+ $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
1832
+
1833
+ $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
1834
+ $this->send_seq_no++;
1835
+
1836
+ if ($this->encrypt !== false) {
1837
+ $packet = $this->encrypt->encrypt($packet);
1838
+ }
1839
+
1840
+ $packet.= $hmac;
1841
+
1842
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
1843
+ $result = strlen($packet) == fputs($this->fsock, $packet);
1844
+ $stop = strtok(microtime(), ' ') + strtok('');
1845
+
1846
+ if (defined('NET_SSH2_LOGGING')) {
1847
+ $temp = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN';
1848
+ $this->message_number_log[] = '-> ' . $temp .
1849
+ ' (' . round($stop - $start, 4) . 's)';
1850
+ if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
1851
+ $this->message_log[] = substr($data, 1);
1852
+ }
1853
+ }
1854
+
1855
+ return $result;
1856
+ }
1857
+
1858
+ /**
1859
+ * Sends channel data
1860
+ *
1861
+ * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
1862
+ *
1863
+ * @param Integer $client_channel
1864
+ * @param String $data
1865
+ * @return Boolean
1866
+ * @access private
1867
+ */
1868
+ function _send_channel_packet($client_channel, $data)
1869
+ {
1870
+ while (strlen($data) > $this->packet_size_client_to_server[$client_channel]) {
1871
+ // resize the window, if appropriate
1872
+ $this->window_size_client_to_server[$client_channel]-= $this->packet_size_client_to_server[$client_channel];
1873
+ if ($this->window_size_client_to_server[$client_channel] < 0) {
1874
+ $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
1875
+ if (!$this->_send_binary_packet($packet)) {
1876
+ return false;
1877
+ }
1878
+ $this->window_size_client_to_server[$client_channel]+= $this->window_size;
1879
+ }
1880
+
1881
+ $packet = pack('CN2a*',
1882
+ NET_SSH2_MSG_CHANNEL_DATA,
1883
+ $this->server_channels[$client_channel],
1884
+ $this->packet_size_client_to_server[$client_channel],
1885
+ $this->_string_shift($data, $this->packet_size_client_to_server[$client_channel])
1886
+ );
1887
+
1888
+ if (!$this->_send_binary_packet($packet)) {
1889
+ return false;
1890
+ }
1891
+ }
1892
+
1893
+ // resize the window, if appropriate
1894
+ $this->window_size_client_to_server[$client_channel]-= strlen($data);
1895
+ if ($this->window_size_client_to_server[$client_channel] < 0) {
1896
+ $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$client_channel], $this->window_size);
1897
+ if (!$this->_send_binary_packet($packet)) {
1898
+ return false;
1899
+ }
1900
+ $this->window_size_client_to_server[$client_channel]+= $this->window_size;
1901
+ }
1902
+
1903
+ return $this->_send_binary_packet(pack('CN2a*',
1904
+ NET_SSH2_MSG_CHANNEL_DATA,
1905
+ $this->server_channels[$client_channel],
1906
+ strlen($data),
1907
+ $data));
1908
+ }
1909
+
1910
+ /**
1911
+ * Disconnect
1912
+ *
1913
+ * @param Integer $reason
1914
+ * @return Boolean
1915
+ * @access private
1916
+ */
1917
+ function _disconnect($reason)
1918
+ {
1919
+ if ($this->bitmap) {
1920
+ $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
1921
+ $this->_send_binary_packet($data);
1922
+ $this->bitmap = 0;
1923
+ fclose($this->fsock);
1924
+ return false;
1925
+ }
1926
+ }
1927
+
1928
+ /**
1929
+ * String Shift
1930
+ *
1931
+ * Inspired by array_shift
1932
+ *
1933
+ * @param String $string
1934
+ * @param optional Integer $index
1935
+ * @return String
1936
+ * @access private
1937
+ */
1938
+ function _string_shift(&$string, $index = 1)
1939
+ {
1940
+ $substr = substr($string, 0, $index);
1941
+ $string = substr($string, $index);
1942
+ return $substr;
1943
+ }
1944
+
1945
+ /**
1946
+ * Define Array
1947
+ *
1948
+ * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
1949
+ * named constants from it, using the value as the name of the constant and the index as the value of the constant.
1950
+ * If any of the constants that would be defined already exists, none of the constants will be defined.
1951
+ *
1952
+ * @param Array $array
1953
+ * @access private
1954
+ */
1955
+ function _define_array()
1956
+ {
1957
+ $args = func_get_args();
1958
+ foreach ($args as $arg) {
1959
+ foreach ($arg as $key=>$value) {
1960
+ if (!defined($value)) {
1961
+ define($value, $key);
1962
+ } else {
1963
+ break 2;
1964
+ }
1965
+ }
1966
+ }
1967
+ }
1968
+
1969
+ /**
1970
+ * Returns a log of the packets that have been sent and received.
1971
+ *
1972
+ * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
1973
+ *
1974
+ * @access public
1975
+ * @return String or Array
1976
+ */
1977
+ function getLog()
1978
+ {
1979
+ if (!defined('NET_SSH2_LOGGING')) {
1980
+ return false;
1981
+ }
1982
+
1983
+ switch (NET_SSH2_LOGGING) {
1984
+ case NET_SSH2_LOG_SIMPLE:
1985
+ return $this->message_number_log;
1986
+ break;
1987
+ case NET_SSH2_LOG_COMPLEX:
1988
+ return $this->_format_log($this->message_log, $this->message_number_log);
1989
+ break;
1990
+ default:
1991
+ return false;
1992
+ }
1993
+ }
1994
+
1995
+ /**
1996
+ * Formats a log for printing
1997
+ *
1998
+ * @param Array $message_log
1999
+ * @param Array $message_number_log
2000
+ * @access private
2001
+ * @return String
2002
+ */
2003
+ function _format_log($message_log, $message_number_log)
2004
+ {
2005
+ static $boundary = ':', $long_width = 65, $short_width = 16;
2006
+
2007
+ $output = '';
2008
+ for ($i = 0; $i < count($message_log); $i++) {
2009
+ $output.= $message_number_log[$i] . "\r\n";
2010
+ $current_log = $message_log[$i];
2011
+ $j = 0;
2012
+ do {
2013
+ if (!empty($current_log)) {
2014
+ $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
2015
+ }
2016
+ $fragment = $this->_string_shift($current_log, $short_width);
2017
+ $hex = substr(
2018
+ preg_replace(
2019
+ '#(.)#es',
2020
+ '"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
2021
+ $fragment),
2022
+ strlen($boundary)
2023
+ );
2024
+ // replace non ASCII printable characters with dots
2025
+ // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
2026
+ // also replace < with a . since < messes up the output on web browsers
2027
+ $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
2028
+ $output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
2029
+ $j++;
2030
+ } while (!empty($current_log));
2031
+ $output.= "\r\n";
2032
+ }
2033
+
2034
+ return $output;
2035
+ }
2036
+
2037
+ /**
2038
+ * Returns all errors
2039
+ *
2040
+ * @return String
2041
+ * @access public
2042
+ */
2043
+ function getErrors()
2044
+ {
2045
+ return $this->errors;
2046
+ }
2047
+
2048
+ /**
2049
+ * Returns the last error
2050
+ *
2051
+ * @return String
2052
+ * @access public
2053
+ */
2054
+ function getLastError()
2055
+ {
2056
+ return $this->errors[count($this->errors) - 1];
2057
+ }
2058
+
2059
+ /**
2060
+ * Return the server identification.
2061
+ *
2062
+ * @return String
2063
+ * @access public
2064
+ */
2065
+ function getServerIdentification()
2066
+ {
2067
+ return $this->server_identifier;
2068
+ }
2069
+
2070
+ /**
2071
+ * Return a list of the key exchange algorithms the server supports.
2072
+ *
2073
+ * @return Array
2074
+ * @access public
2075
+ */
2076
+ function getKexAlgorithms()
2077
+ {
2078
+ return $this->kex_algorithms;
2079
+ }
2080
+
2081
+ /**
2082
+ * Return a list of the host key (public key) algorithms the server supports.
2083
+ *
2084
+ * @return Array
2085
+ * @access public
2086
+ */
2087
+ function getServerHostKeyAlgorithms()
2088
+ {
2089
+ return $this->server_host_key_algorithms;
2090
+ }
2091
+
2092
+ /**
2093
+ * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
2094
+ *
2095
+ * @return Array
2096
+ * @access public
2097
+ */
2098
+ function getEncryptionAlgorithmsClient2Server()
2099
+ {
2100
+ return $this->encryption_algorithms_client_to_server;
2101
+ }
2102
+
2103
+ /**
2104
+ * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
2105
+ *
2106
+ * @return Array
2107
+ * @access public
2108
+ */
2109
+ function getEncryptionAlgorithmsServer2Client()
2110
+ {
2111
+ return $this->encryption_algorithms_server_to_client;
2112
+ }
2113
+
2114
+ /**
2115
+ * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
2116
+ *
2117
+ * @return Array
2118
+ * @access public
2119
+ */
2120
+ function getMACAlgorithmsClient2Server()
2121
+ {
2122
+ return $this->mac_algorithms_client_to_server;
2123
+ }
2124
+
2125
+ /**
2126
+ * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
2127
+ *
2128
+ * @return Array
2129
+ * @access public
2130
+ */
2131
+ function getMACAlgorithmsServer2Client()
2132
+ {
2133
+ return $this->mac_algorithms_server_to_client;
2134
+ }
2135
+
2136
+ /**
2137
+ * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
2138
+ *
2139
+ * @return Array
2140
+ * @access public
2141
+ */
2142
+ function getCompressionAlgorithmsClient2Server()
2143
+ {
2144
+ return $this->compression_algorithms_client_to_server;
2145
+ }
2146
+
2147
+ /**
2148
+ * Return a list of the compression algorithms the server supports, when sending stuff to the client.
2149
+ *
2150
+ * @return Array
2151
+ * @access public
2152
+ */
2153
+ function getCompressionAlgorithmsServer2Client()
2154
+ {
2155
+ return $this->compression_algorithms_server_to_client;
2156
+ }
2157
+
2158
+ /**
2159
+ * Return a list of the languages the server supports, when sending stuff to the client.
2160
+ *
2161
+ * @return Array
2162
+ * @access public
2163
+ */
2164
+ function getLanguagesServer2Client()
2165
+ {
2166
+ return $this->languages_server_to_client;
2167
+ }
2168
+
2169
+ /**
2170
+ * Return a list of the languages the server supports, when receiving stuff from the client.
2171
+ *
2172
+ * @return Array
2173
+ * @access public
2174
+ */
2175
+ function getLanguagesClient2Server()
2176
+ {
2177
+ return $this->languages_client_to_server;
2178
+ }
2179
+
2180
+ /**
2181
+ * Returns the server public host key.
2182
+ *
2183
+ * Caching this the first time you connect to a server and checking the result on subsequent connections
2184
+ * is recommended. Returns false if the server signature is not signed correctly with the public host key.
2185
+ *
2186
+ * @return Mixed
2187
+ * @access public
2188
+ */
2189
+ function getServerPublicHostKey()
2190
+ {
2191
+ $signature = $this->signature;
2192
+ $server_public_host_key = $this->server_public_host_key;
2193
+
2194
+ extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
2195
+ $this->_string_shift($server_public_host_key, $length);
2196
+
2197
+ switch ($this->signature_format) {
2198
+ case 'ssh-dss':
2199
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2200
+ $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2201
+
2202
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2203
+ $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2204
+
2205
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2206
+ $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2207
+
2208
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2209
+ $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2210
+
2211
+ /* The value for 'dss_signature_blob' is encoded as a string containing
2212
+ r, followed by s (which are 160-bit integers, without lengths or
2213
+ padding, unsigned, and in network byte order). */
2214
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
2215
+ if ($temp['length'] != 40) {
2216
+ user_error('Invalid signature', E_USER_NOTICE);
2217
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
2218
+ }
2219
+
2220
+ $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
2221
+ $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
2222
+
2223
+ if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
2224
+ user_error('Invalid signature', E_USER_NOTICE);
2225
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
2226
+ }
2227
+
2228
+ $w = $s->modInverse($q);
2229
+
2230
+ $u1 = $w->multiply(new Math_BigInteger(sha1($this->session_id), 16));
2231
+ list(, $u1) = $u1->divide($q);
2232
+
2233
+ $u2 = $w->multiply($r);
2234
+ list(, $u2) = $u2->divide($q);
2235
+
2236
+ $g = $g->modPow($u1, $p);
2237
+ $y = $y->modPow($u2, $p);
2238
+
2239
+ $v = $g->multiply($y);
2240
+ list(, $v) = $v->divide($p);
2241
+ list(, $v) = $v->divide($q);
2242
+
2243
+ if (!$v->equals($r)) {
2244
+ user_error('Bad server signature', E_USER_NOTICE);
2245
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
2246
+ }
2247
+
2248
+ break;
2249
+ case 'ssh-rsa':
2250
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2251
+ $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2252
+
2253
+ $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
2254
+ $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
2255
+ $nLength = $temp['length'];
2256
+
2257
+ /*
2258
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
2259
+ $signature = $this->_string_shift($signature, $temp['length']);
2260
+
2261
+ if (!class_exists('Crypt_RSA')) {
2262
+ require_once('Crypt/RSA.php');
2263
+ }
2264
+
2265
+ $rsa = new Crypt_RSA();
2266
+ $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
2267
+ $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
2268
+ if (!$rsa->verify($this->session_id, $signature)) {
2269
+ user_error('Bad server signature', E_USER_NOTICE);
2270
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
2271
+ }
2272
+ */
2273
+
2274
+ $temp = unpack('Nlength', $this->_string_shift($signature, 4));
2275
+ $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
2276
+
2277
+ // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
2278
+ // following URL:
2279
+ // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
2280
+
2281
+ // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
2282
+
2283
+ if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
2284
+ user_error('Invalid signature', E_USER_NOTICE);
2285
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
2286
+ }
2287
+
2288
+ $s = $s->modPow($e, $n);
2289
+ $s = $s->toBytes();
2290
+
2291
+ $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->session_id));
2292
+ $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h;
2293
+
2294
+ if ($s != $h) {
2295
+ user_error('Bad server signature', E_USER_NOTICE);
2296
+ return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
2297
+ }
2298
+ }
2299
+
2300
+ return $this->server_public_host_key;
2301
+ }
2302
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/PHP/Compat/Function/array_fill.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // $Id: array_fill.php,v 1.1 2007/07/02 04:19:55 terrafrost Exp $
3
+
4
+
5
+ /**
6
+ * Replace array_fill()
7
+ *
8
+ * @category PHP
9
+ * @package PHP_Compat
10
+ * @license LGPL - http://www.gnu.org/licenses/lgpl.html
11
+ * @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
12
+ * @link http://php.net/function.array_fill
13
+ * @author Jim Wigginton <terrafrost@php.net>
14
+ * @version $Revision: 1.1 $
15
+ * @since PHP 4.2.0
16
+ */
17
+ function php_compat_array_fill($start_index, $num, $value)
18
+ {
19
+ if ($num <= 0) {
20
+ user_error('array_fill(): Number of elements must be positive', E_USER_WARNING);
21
+
22
+ return false;
23
+ }
24
+
25
+ $temp = array();
26
+
27
+ $end_index = $start_index + $num;
28
+ for ($i = (int) $start_index; $i < $end_index; $i++) {
29
+ $temp[$i] = $value;
30
+ }
31
+
32
+ return $temp;
33
+ }
34
+
35
+ // Define
36
+ if (!function_exists('array_fill')) {
37
+ function array_fill($start_index, $num, $value)
38
+ {
39
+ return php_compat_array_fill($start_index, $num, $value);
40
+ }
41
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/PHP/Compat/Function/bcpowmod.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // $Id: bcpowmod.php,v 1.1 2007/07/02 04:19:55 terrafrost Exp $
3
+
4
+
5
+ /**
6
+ * Replace bcpowmod()
7
+ *
8
+ * @category PHP
9
+ * @package PHP_Compat
10
+ * @license LGPL - http://www.gnu.org/licenses/lgpl.html
11
+ * @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
12
+ * @link http://php.net/function.bcpowmod
13
+ * @author Sara Golemon <pollita@php.net>
14
+ * @version $Revision: 1.1 $
15
+ * @since PHP 5.0.0
16
+ * @require PHP 4.0.0 (user_error)
17
+ */
18
+ function php_compat_bcpowmod($x, $y, $modulus, $scale = 0)
19
+ {
20
+ // Sanity check
21
+ if (!is_scalar($x)) {
22
+ user_error('bcpowmod() expects parameter 1 to be string, ' .
23
+ gettype($x) . ' given', E_USER_WARNING);
24
+ return false;
25
+ }
26
+
27
+ if (!is_scalar($y)) {
28
+ user_error('bcpowmod() expects parameter 2 to be string, ' .
29
+ gettype($y) . ' given', E_USER_WARNING);
30
+ return false;
31
+ }
32
+
33
+ if (!is_scalar($modulus)) {
34
+ user_error('bcpowmod() expects parameter 3 to be string, ' .
35
+ gettype($modulus) . ' given', E_USER_WARNING);
36
+ return false;
37
+ }
38
+
39
+ if (!is_scalar($scale)) {
40
+ user_error('bcpowmod() expects parameter 4 to be integer, ' .
41
+ gettype($scale) . ' given', E_USER_WARNING);
42
+ return false;
43
+ }
44
+
45
+ $t = '1';
46
+ while (bccomp($y, '0')) {
47
+ if (bccomp(bcmod($y, '2'), '0')) {
48
+ $t = bcmod(bcmul($t, $x), $modulus);
49
+ $y = bcsub($y, '1');
50
+ }
51
+
52
+ $x = bcmod(bcmul($x, $x), $modulus);
53
+ $y = bcdiv($y, '2');
54
+ }
55
+
56
+ return $t;
57
+ }
58
+
59
+
60
+ // Define
61
+ if (!function_exists('bcpowmod')) {
62
+ function bcpowmod($x, $y, $modulus, $scale = 0)
63
+ {
64
+ return php_compat_bcpowmod($x, $y, $modulus, $scale);
65
+ }
66
+ }
app/code/local/Wisepricer/Syncer/lib/phpseclib/PHP/Compat/Function/str_split.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Replace str_split()
4
+ *
5
+ * @category PHP
6
+ * @package PHP_Compat
7
+ * @license LGPL - http://www.gnu.org/licenses/lgpl.html
8
+ * @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
9
+ * @link http://php.net/function.str_split
10
+ * @author Aidan Lister <aidan@php.net>
11
+ * @version $Revision: 1.1 $
12
+ * @since PHP 5
13
+ * @require PHP 4.0.0 (user_error)
14
+ */
15
+ function php_compat_str_split($string, $split_length = 1)
16
+ {
17
+ if (!is_scalar($split_length)) {
18
+ user_error('str_split() expects parameter 2 to be long, ' .
19
+ gettype($split_length) . ' given', E_USER_WARNING);
20
+ return false;
21
+ }
22
+
23
+ $split_length = (int) $split_length;
24
+ if ($split_length < 1) {
25
+ user_error('str_split() The length of each segment must be greater than zero', E_USER_WARNING);
26
+ return false;
27
+ }
28
+
29
+ // Select split method
30
+ if ($split_length < 65536) {
31
+ // Faster, but only works for less than 2^16
32
+ preg_match_all('/.{1,' . $split_length . '}/s', $string, $matches);
33
+ return $matches[0];
34
+ } else {
35
+ // Required due to preg limitations
36
+ $arr = array();
37
+ $idx = 0;
38
+ $pos = 0;
39
+ $len = strlen($string);
40
+
41
+ while ($len > 0) {
42
+ $blk = ($len < $split_length) ? $len : $split_length;
43
+ $arr[$idx++] = substr($string, $pos, $blk);
44
+ $pos += $blk;
45
+ $len -= $blk;
46
+ }
47
+
48
+ return $arr;
49
+ }
50
+ }
51
+
52
+
53
+ // Define
54
+ if (!function_exists('str_split')) {
55
+ function str_split($string, $split_length = 1)
56
+ {
57
+ return php_compat_str_split($string, $split_length);
58
+ }
59
+ }
app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-install-0.1.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $installer->run("
8
+
9
+ CREATE TABLE IF NOT EXISTS {$this->getTable('wisepricer_syncer_config')} (
10
+
11
+ `licensekey_id` int(11) NOT NULL auto_increment,
12
+
13
+ `licensekey` varchar(255) character set utf8 NOT NULL,
14
+
15
+ `is_confirmed` smallint(5) unsigned NOT NULL default '0',
16
+
17
+ `publickey` text character set utf8 NOT NULL,
18
+
19
+ `privatekey` text character set utf8 NOT NULL,
20
+
21
+ PRIMARY KEY (`licensekey_id`)
22
+
23
+ ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
24
+
25
+ ");
26
+
27
+ $installer->endSetup();
app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-install-1.0.0.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $installer->run("
8
+
9
+ CREATE TABLE IF NOT EXISTS {$this->getTable('wisepricer_syncer_config')} (
10
+
11
+ `licensekey_id` int(11) NOT NULL auto_increment,
12
+
13
+ `licensekey` varchar(255) character set utf8 NOT NULL,
14
+
15
+ `is_confirmed` smallint(5) unsigned NOT NULL default '0',
16
+
17
+ `publickey` text character set utf8 NOT NULL,
18
+
19
+ `privatekey` text character set utf8 NOT NULL,
20
+
21
+ PRIMARY KEY (`licensekey_id`)
22
+
23
+ ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
24
+
25
+ ");
26
+
27
+ $installer->endSetup();
app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-install-1.0.2.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $installer->run("
8
+
9
+ CREATE TABLE IF NOT EXISTS {$this->getTable('wisepricer_syncer_config')} (
10
+
11
+ `licensekey_id` int(11) NOT NULL auto_increment,
12
+
13
+ `licensekey` varchar(255) character set utf8 NOT NULL,
14
+
15
+ `is_confirmed` smallint(5) unsigned NOT NULL default '0',
16
+
17
+ `publickey` text character set utf8 NOT NULL,
18
+
19
+ `privatekey` text character set utf8 NOT NULL,
20
+
21
+ PRIMARY KEY (`licensekey_id`)
22
+
23
+ ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
24
+
25
+ ");
26
+
27
+ $installer->endSetup();
app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-upgrade-0.1-0.2.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ $installer = $this;
5
+ $installer->startSetup();
6
+
7
+ $installer->run("
8
+
9
+ CREATE TABLE IF NOT EXISTS {$this->getTable('wisepricer_syncer_mapping')} (
10
+
11
+ `mapping_id` int(11) NOT NULL auto_increment,
12
+
13
+ `wsp_field` varchar(255) character set utf8 NOT NULL,
14
+
15
+ `magento_field` varchar(255) character set utf8 NOT NULL,
16
+
17
+ PRIMARY KEY (`mapping_id`),
18
+
19
+ UNIQUE KEY `wsp_field` (`magento_field`)
20
+
21
+ ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
22
+
23
+
24
+ ");
25
+
26
+ /* right before this */
27
+ $installer->endSetup();
28
+
29
+ ?>
app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-upgrade-1.0.0-1.1.0.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ $installer = $this;
5
+ $installer->startSetup();
6
+
7
+ $installer->run("
8
+
9
+ CREATE TABLE IF NOT EXISTS {$this->getTable('wisepricer_syncer_mapping')} (
10
+
11
+ `mapping_id` int(11) NOT NULL auto_increment,
12
+
13
+ `wsp_field` varchar(255) character set utf8 NOT NULL,
14
+
15
+ `magento_field` varchar(255) character set utf8 NOT NULL,
16
+
17
+ PRIMARY KEY (`mapping_id`),
18
+
19
+ UNIQUE KEY `wsp_field` (`magento_field`)
20
+
21
+ ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
22
+
23
+
24
+ ");
25
+
26
+ /* right before this */
27
+ $installer->endSetup();
28
+
29
+ ?>
app/code/local/Wisepricer/Syncer/sql/syncer_setup/mysql4-upgrade-1.0.2-1.1.1.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ $installer = $this;
5
+ $installer->startSetup();
6
+
7
+ $installer->run("
8
+
9
+ CREATE TABLE IF NOT EXISTS {$this->getTable('wisepricer_syncer_mapping')} (
10
+
11
+ `mapping_id` int(11) NOT NULL auto_increment,
12
+
13
+ `wsp_field` varchar(255) character set utf8 NOT NULL,
14
+
15
+ `magento_field` varchar(255) character set utf8 NOT NULL,
16
+
17
+ `extra` varchar(255) character set utf8 NULL,
18
+
19
+ PRIMARY KEY (`mapping_id`),
20
+
21
+ UNIQUE KEY `wsp_field` (`wsp_field`)
22
+
23
+ ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
24
+
25
+
26
+ ");
27
+
28
+ /* right before this */
29
+ $installer->endSetup();
30
+
31
+ ?>
app/design/adminhtml/default/default/layout/syncer.xml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <adminhtml_syncer_mapping>
4
+ <reference name="head">
5
+ <action method="addItem"><type>skin_js</type><name>wisepricer/prototype17.js</name></action>
6
+ <action method="addItem"><type>skin_js</type><name>wisepricer/chosen.proto.js</name></action>
7
+ <action method="addCss"><stylesheet>wisepricer/wisepricer.css</stylesheet></action>
8
+ <action method="addCss"><stylesheet>wisepricer/chosen.css</stylesheet></action>
9
+ <action method="addItem"><type>skin_js</type><name>wisepricer/chosen.proto.js</name></action>
10
+ </reference>
11
+ <reference name="content">
12
+ <block type="syncer/adminhtml_mapping" template="wisepricer/mapping.phtml" name="wisepricer.mapping" />
13
+ </reference>
14
+ </adminhtml_syncer_mapping>
15
+ </layout>
app/design/adminhtml/default/default/template/wisepricer/mapping.phtml ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $adminobj= new Mage_Adminhtml_Block_Template();
3
+ $formkey=$adminobj->getFormKey();
4
+ $licenseData=Mage::getModel('wisepricer_syncer/config')->load(1);
5
+ ?>
6
+ <form id="mapping_form" name="mapping_form" method="post" action="<?php echo $this->getUrl('*/*/savemapping')?>">
7
+ <input name="form_key" type="hidden" value="<?php echo Mage::getSingleton('core/session')->getFormKey() ?>" />
8
+ <div class="top_block">
9
+ <div class="top_block_left">
10
+ <div class="inner-cont">
11
+ <a href="http://www.wisepricer.com" target="_blank"><img src="<?php echo $this->getSkinUrl('images/wisepricer/wp-logo.png'); ?>"/></a>
12
+ </div>
13
+ </div>
14
+ <div class="top_block_right">
15
+ <button class="wp-save-btn" onclick="return beforeSubmit()"></button>
16
+ </div>
17
+ </div>
18
+ <div class="mid_block">
19
+ <div class="mid_block_left">
20
+ <div class="guidelines">
21
+ <div class="guidelines-block">
22
+ <p><?php echo $this->__('Enter your WisePricer license key you received from WisePricer. This will link this store with your WisePricer account.')?></p>
23
+ </div>
24
+ <div class="guidelines-block mapping-guidelines">
25
+ <h4><?php echo $this->__('Wisepricer mapping guidelines:')?></h4>
26
+ <ul>
27
+ <li id="rule1"><?php echo $this->__('If you have <span class="field">UPC</span> or <span class="field">ASIN</span> then fields <span class="field">BRAND</span>,<span class="field">MODEL</span>,<span class="field">MPN</span> are not required.')?></li>
28
+ <li id="rule2"><?php echo $this->__('If you don\'t have <span class="field">UPC</span> or <span class="field">ASIN</span>, then you should have at least one of the following combinations: <span class="field">BRAND+MODEL</span> or <span class="field">BRAND+MPN</span>')?></li>
29
+ <li id="rule3"><?php echo $this->__('If you must have <span class="field">COST</span> or <span class="field">MINPRICE</span>, you can set fixed rule for <span class="field">MINPRICE</span>')?></li>
30
+ <li><?php echo $this->__('<span class="field">NAME</span>, <span class="field">SKU</span>, and <span class="field">PRICE</span> fields are required')?></li>
31
+ <li><?php echo $this->__('Other fields are not required, but having them will increase your ability to interact with them within WisePricer(e.g. setting advanced rules, better matching and more)')?></li>
32
+ </ul>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <div class="mid_block_right">
37
+ <div class="values-block">
38
+ <div class="license-block">
39
+ <div class="label-block"><label><?php echo $this->__('Enter your License key')?> <span class="required">*</span></label></div>
40
+ <input type="text" value="<?php echo $licenseData->getLicensekey()?>" class="input-text required-entry" name="register_form[licensekey]" />
41
+ </div>
42
+ </div>
43
+ <p><?php echo $this->__('Please make sure the following fields are assigned correctly:');?></p>
44
+ <div class="values-block">
45
+ <table cellspacing="0" class="form-list">
46
+ <tr class="t-header">
47
+ <td><strong><?php echo $this->__('Required Fields')?></strong></td><td><strong><?php echo $this->__('Detected field')?></strong></td>
48
+ </tr>
49
+ <tr>
50
+ <td class="label firstcol"><span><?php echo $this->__('Title')?><span class="required">*</span></span></td>
51
+ <td class="input-ele"><?php echo $this->renderAttributesSelect('mapping_form[title]','title','name','required-entry chzn-select')?></td>
52
+ <td></td>
53
+ </tr>
54
+ <tr>
55
+ <td class="label firstcol"><span><?php echo $this->__('SKU')?><span class="required">*</span></span></td>
56
+ <td class="input-ele"><?php echo $this->renderAttributesSelect('mapping_form[sku]','sku','sku','required-entry chzn-select')?></td>
57
+ <td></td>
58
+ </tr>
59
+ <tr>
60
+ <td class="label firstcol"><span><?php echo $this->__('UPC')?></span></td>
61
+ <td class="input-ele">
62
+ <?php echo $this->renderAttributesSelect('mapping_form[upc]','upc','upc','chzn-select')?>
63
+
64
+ </td>
65
+ <td class="desc"><p><span><?php echo $this->__('Universal Product Code')?></span></p></td>
66
+ </tr>
67
+ <tr>
68
+ <td class="label firstcol"><span><?php echo $this->__('Brand')?></span></td>
69
+ <td class="input-ele"><?php echo $this->renderAttributesSelect('mapping_form[brand]','brand','brand','chzn-select')?></td>
70
+ <td></td>
71
+ </tr>
72
+ <tr>
73
+ <td class="label firstcol"><span><?php echo $this->__('Model')?></span></td>
74
+ <td class="input-ele"><?php echo $this->renderAttributesSelect('mapping_form[model]','model','model','chzn-select')?></td>
75
+ <td></td>
76
+ </tr>
77
+ <tr>
78
+ <td class="label firstcol"><span><?php echo $this->__('MPN')?></span></td>
79
+ <td class="input-ele"><?php echo $this->renderAttributesSelect('mapping_form[mpn]','mpn','mpn','chzn-select')?></td>
80
+ <td></td>
81
+ </tr>
82
+ <tr>
83
+ <td class="label firstcol"><span><?php echo $this->__('ASIN')?></span></td>
84
+ <td class="input-ele">
85
+ <?php echo $this->renderAttributesSelect('mapping_form[asin]','asin','asin','chzn-select')?>
86
+
87
+ </td>
88
+ <td class="desc"><p><span><?php echo $this->__('Amazon Standard Identification Number')?></span></p></td>
89
+ </tr>
90
+ <tr>
91
+ <td class="label firstcol"><span><?php echo $this->__('Price')?> <span class="required">*</span></span></td>
92
+ <td class="input-ele"><?php echo $this->renderAttributesSelect('mapping_form[price]','price','price','required-entry chzn-select')?></td>
93
+ <td></td>
94
+ </tr>
95
+ <tr>
96
+ <td class="label firstcol"><span><?php echo $this->__('Cost')?></span></td>
97
+ <td class="input-ele"><?php echo $this->renderAttributesSelect('mapping_form[cost]','cost','0','chzn-select')?></td>
98
+ <td></td>
99
+ </tr>
100
+ <tr>
101
+ <td class="label firstcol"><span><?php echo $this->__('Stock')?></span></td>
102
+ <td class="input-ele">
103
+ <?php echo $this->renderAttributesSelect('mapping_form[stock]','stock','qty','chzn-select')?>
104
+ </td>
105
+ <td class="desc">
106
+ <p><span><?php echo $this->__('Product quantity')?></span></p>
107
+ </td>
108
+ </tr>
109
+ <tr>
110
+ <td class="label firstcol"><span><?php echo $this->__('Product image')?></span></td>
111
+ <td class="input-ele"><?php echo $this->renderAttributesSelect('mapping_form[productimage]','productimage','image','chzn-select')?></td>
112
+ <td></td>
113
+ </tr>
114
+ <tr class="special-opt-cont">
115
+ <td colspan="2">
116
+ <div class="ship-price-cont minprice">
117
+ <div class="ship-price-cont-top">
118
+ <div class="label"><?php echo $this->__('MinPrice')?></div>
119
+ <div class="value"><?php echo $this->renderAttributesSelect('mapping_form[minprice]','minprice','cost','chzn-select')?></div>
120
+ </div>
121
+ <div class="ship-price-cont-mid"><span><?php echo $this->__('-Or-')?></span></div>
122
+ <div class="ship-price-cont-bott">
123
+ <div class="label"><?php echo $this->__('Set a minimum price:')?></div>
124
+ </div>
125
+ <div class="ship-price-cont-bott desc">
126
+ <p><span><?php echo $this->__('this will manually override the choosen attribute.')?></span></p>
127
+ </div>
128
+ <div class="ship-price-cont-bott">
129
+ <div class="label">
130
+ <input id="minprice_man" type="text" name="mapping_form[minprice_man]" value="<?php echo $this->getFixedMinPrice()?>"/>
131
+ <?php echo $this->renderMinPriceRuleSelects();?>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </td>
136
+ <td class="desc"><p><span><?php echo $this->__('Wisepricer will never price below the minimum price you set.')?></span></p></td>
137
+ </tr>
138
+ <tr>
139
+ <td colspan="2">
140
+ <div class="ship-price-cont">
141
+ <div class="ship-price-cont-top">
142
+ <div class="label"><?php echo $this->__('Shipping price')?></div>
143
+ <div class="value"><?php echo $this->renderAttributesSelect('mapping_form[shipping]','shipping','0','chzn-select')?></div>
144
+ </div>
145
+ <div class="ship-price-cont-mid"><span><?php echo $this->__('-Or-')?></span></div>
146
+ <div class="ship-price-cont-bott">
147
+ <div class="label"><?php echo $this->__('Enter fixed shipping price')?></div>
148
+ <div class="value"><input type="text" name="mapping_form[shipping_man]" value="<?php echo $this->getShippingFixedRate()?>"/></div>
149
+ </div>
150
+ <div class="ship-price-cont-bott desc">
151
+ <p><span><?php echo $this->__('this will manually override the choosen attribute.')?></span></p>
152
+ </div>
153
+ </div>
154
+ </td>
155
+ <td></td>
156
+ </tr>
157
+ </table>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ <div class="bott_block">
162
+ <div class="bott_block_left"></div>
163
+ <div class="bott_block_right">
164
+ <button class="wp-save-btn" onclick="return beforeSubmit()"></button>
165
+ </div>
166
+ </div>
167
+ </form>
168
+ <style>
169
+ #wsp-mapping td{
170
+ line-height: 20px;
171
+ overflow: hidden;
172
+ padding: 0 0 20px;
173
+ vertical-align: top;
174
+ width: 230px;
175
+ }
176
+ #wsp-mapping td.second-val{
177
+ padding-left: 20px
178
+ }
179
+ #wsp-mapping{
180
+ padding-top: 20px
181
+ }
182
+ </style>
183
+ <script type="text/javascript">
184
+
185
+ document.observe('dom:loaded', function(evt) {
186
+ var select, selects, _i, _len, _results;
187
+ if (Prototype.Browser.IE && (Prototype.BrowserFeatures['Version'] === 6 || Prototype.BrowserFeatures['Version'] === 7)) {
188
+ return;
189
+ }
190
+ selects = $$(".chzn-select");
191
+ _results = [];
192
+ for (_i = 0, _len = selects.length; _i < _len; _i++) {
193
+ select = selects[_i];
194
+ _results.push(new Chosen(select,{disable_search_threshold: 50}));
195
+ }
196
+ deselects = $$(".chzn-select-deselect");
197
+ for (_i = 0, _len = deselects.length; _i < _len; _i++) {
198
+ select = deselects[_i];
199
+ _results.push(new Chosen(select,{allow_single_deselect:true}));
200
+ }
201
+ return _results;
202
+ });
203
+
204
+ var editForm = new varienForm('mapping_form');
205
+
206
+ function beforeSubmit(){
207
+ $('rule2').setStyle('border:none');
208
+ $('rule1').setStyle('border:none');
209
+ $('rule3').setStyle('border:none');
210
+ var upc=$('upc').value;
211
+ var brand=$('brand').value;
212
+ var model=$('model').value;
213
+ var asin=$('asin').value;
214
+ var mpn=$('mpn').value;
215
+ var minprice=$('minprice').value;
216
+ var cost=$('cost').value;
217
+ var minprice_man=$('minprice_man').value;
218
+
219
+ var isValid=false;
220
+ var failedRule=0;
221
+ if(upc!=''||asin!=''){
222
+
223
+ if(minprice!=''||(cost!=''&&minprice_man!='')){
224
+ editForm.submit();
225
+ return;
226
+ }else{
227
+ failedRule=3;
228
+ }
229
+ }
230
+
231
+ if((brand!=''&&model!='')||(brand!=''&&mpn!='')){
232
+
233
+ if(minprice!=''||(cost!=''&&minprice_man!='')){
234
+ editForm.submit();
235
+ return;
236
+ }else{
237
+ failedRule=3;
238
+ }
239
+ }
240
+
241
+ if(failedRule==3){
242
+ $('rule3').setStyle('border:red solid 1px');
243
+
244
+ }else{
245
+ $('rule2').setStyle('border:red solid 1px');
246
+ $('rule1').setStyle('border:red solid 1px');
247
+ }
248
+
249
+ alert(Translator.translate('Please follow instructions in your mapping listed on the left.'));
250
+ window.location.hash='rule1';
251
+
252
+ return false;
253
+
254
+ }
255
+ </script>
app/etc/modules/Wisepricer_Syncer.xml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+
4
+
5
+ <modules>
6
+
7
+
8
+ <Wisepricer_Syncer>
9
+
10
+
11
+ <active>true</active>
12
+
13
+
14
+ <codePool>local</codePool>
15
+
16
+
17
+ </Wisepricer_Syncer>
18
+
19
+
20
+ </modules>
21
+
22
+
23
+ </config>
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Wisepricer_Syncer</name>
4
+ <version>1.1.2.5</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://www.opensource.org/licenses/osl-3.0.php">OSL</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>WisePricer- Beat your competition</summary>
10
+ <description>WisePricer is a new tool that allows you to Track &amp; monitor successful online retailers and update your store prices in real-time. With WisePricer you&#x2019;ll never get left behind the competition.</description>
11
+ <notes>wisepricer</notes>
12
+ <authors><author><name>Moshe</name><user>moshewp</user><email>moshe@wisepricer.com</email></author></authors>
13
+ <date>2012-09-12</date>
14
+ <time>13:17:45</time>
15
+ <contents><target name="magelocal"><dir name="Wisepricer"><dir name="Syncer"><dir name="Block"><dir name="Adminhtml"><file name="Mapping.php" hash="ce3249eb8500db5ba9a60eb788437fed"/></dir></dir><dir name="Helper"><file name="Data.php" hash="025b73c04ab0ca01d2e7c75aaad7fea6"/></dir><dir name="Model"><dir name="Adminhtml"><file name="Attributes.php" hash="2d50a30f0e7df8cf5791aea101f9008a"/></dir><file name="Config.php" hash="d669c3dc977ddf71a58c90fa8df3180c"/><file name="Mapping.php" hash="d924ae8bcf54a3ca1224e8680d847fee"/><dir name="Mysql4"><dir name="Config"><file name="Collection.php" hash="c7c7b6844e3ff8893163c392f4132f30"/></dir><file name="Config.php" hash="61b7eb73489844aa0ee041c216bab2db"/><dir name="Mapping"><file name="Collection.php" hash="c0f15143db582e070cfb83de92c57d09"/></dir><file name="Mapping.php" hash="4d57fac75395214a3ebef8a498e9c7d2"/></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="SyncerController.php" hash="453e3352d35836128a24280471362866"/></dir><file name="ProductsController.php" hash="80fb7e0d52d05905002785abb4359ebb"/></dir><dir name="etc"><file name="config.xml" hash="52b4ece28b4c25fd867f60b913a294fb"/></dir><dir name="lib"><dir name="phpseclib"><dir name="Crypt"><file name="AES.php" hash="6af09e1f2da7eb069760d788ee0d54e0"/><file name="DES.php" hash="1b75baa9ddad68309b8e82a6cebc8dc1"/><file name="Hash.php" hash="f8ca80e79e2d570afeb2f78023f213d4"/><file name="RC4.php" hash="2e507f5635b6d98d9daaa341a8a6fb8b"/><file name="RSA.php" hash="579c11f7b2e05cae01b67b31461ca264"/><file name="Random.php" hash="6e86dfbd3f8f19f32f13a53a756e53af"/><file name="Rijndael.php" hash="3a0aa237cbc23ffba12aad6dd5215ab8"/><file name="TripleDES.php" hash="db12eebd6ed7290e4ac0d521ab5c55ce"/></dir><dir name="Math"><file name="BigInteger.php" hash="8dd950e566864b28c92b58acb06e1535"/></dir><dir name="Net"><file name="SFTP.php" hash="4665bea6e721ec016e0e70207cf45339"/><file name="SSH1.php" hash="19155c50cf00370152905b2a4a6ce657"/><file name="SSH2.php" hash="65563b38d6e6df3e8ebc628cec86f86d"/></dir><dir name="PHP"><dir name="Compat"><dir name="Function"><file name="array_fill.php" hash="2c0a4268deed10ced9491155e2284a82"/><file name="bcpowmod.php" hash="37fc1de34085874a930eaa76888f0cb1"/><file name="str_split.php" hash="889ab2b0d90b0962ded2ee65f1bfbc37"/></dir></dir></dir></dir></dir><dir name="sql"><dir name="syncer_setup"><file name="mysql4-install-0.1.php" hash="bec70d78d77f96bd3d06881d00f344b4"/><file name="mysql4-install-1.0.0.php" hash="bec70d78d77f96bd3d06881d00f344b4"/><file name="mysql4-install-1.0.2.php" hash="bec70d78d77f96bd3d06881d00f344b4"/><file name="mysql4-upgrade-0.1-0.2.php" hash="667ab4612d4885f052568f4142e92eb7"/><file name="mysql4-upgrade-1.0.0-1.1.0.php" hash="667ab4612d4885f052568f4142e92eb7"/><file name="mysql4-upgrade-1.0.2-1.1.1.php" hash="1b80bc591baf656cd5e275c3121e04c1"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Wisepricer_Syncer.xml" hash="838dc229469d27db4c96a49591b12f55"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="syncer.xml" hash="a9d0f0b5af6b7bc28fb3c3b897c1773c"/></dir><dir name="template"><dir name="wisepricer"><file name="mapping.phtml" hash="8c8f177dc6f5097c422c6bc783542b5f"/></dir></dir></dir></dir></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="images"><dir name="wisepricer"><file name="bullet-green.png" hash="78d917a9d9aea11366bada6e0ae53931"/><file name="validation_advice_bg.gif" hash="ffdad80de989e3b04a977be3778c4347"/><file name="wp-alert-icon.png" hash="0dbbadfbbe2329098d03f8351aa2eaf2"/><file name="wp-logo.png" hash="48db98cdfc570336c942271352f31094"/><file name="wp-save-btn.png" hash="6d8e02c7f5e54dcc705e6436f126c66d"/></dir></dir><dir name="wisepricer"><file name="chosen-sprite.png" hash="8e70d120437ffc6a1bf7cebeca292d5c"/><file name="chosen.css" hash="a1b7280ed62dbe210257027dd6bfa073"/><file name="chosen.proto.js" hash="b98b346e60d90e7e3f83106cf860dacd"/><file name="myprototype.js" hash="3b4b13dad33b475e11feb26fd3468ecc"/><file name="prototype17.js" hash="3b4b13dad33b475e11feb26fd3468ecc"/><file name="wisepricer.css" hash="bab3eada0e2a974b1f8f7a87cbf5bb9c"/></dir></dir></dir></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php></required></dependencies>
18
+ </package>
skin/adminhtml/default/default/images/wisepricer/bullet-green.png ADDED
Binary file
skin/adminhtml/default/default/images/wisepricer/validation_advice_bg.gif ADDED
Binary file
skin/adminhtml/default/default/images/wisepricer/wp-alert-icon.png ADDED
Binary file
skin/adminhtml/default/default/images/wisepricer/wp-logo.png ADDED
Binary file
skin/adminhtml/default/default/images/wisepricer/wp-save-btn.png ADDED
Binary file
skin/adminhtml/default/default/wisepricer/chosen-sprite.png ADDED
Binary file
skin/adminhtml/default/default/wisepricer/chosen.css ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* @group Base */
2
+ .chzn-container {
3
+ font-size: 13px;
4
+ position: relative;
5
+ display: inline-block;
6
+ zoom: 1;
7
+ *display: inline;
8
+ }
9
+ .chzn-container .chzn-drop {
10
+ background: #fff;
11
+ border: 1px solid #aaa;
12
+ border-top: 0;
13
+ position: absolute;
14
+ top: 29px;
15
+ left: 0;
16
+ -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15);
17
+ -moz-box-shadow : 0 4px 5px rgba(0,0,0,.15);
18
+ -o-box-shadow : 0 4px 5px rgba(0,0,0,.15);
19
+ box-shadow : 0 4px 5px rgba(0,0,0,.15);
20
+ z-index: 1010;
21
+ }
22
+ /* @end */
23
+
24
+ /* @group Single Chosen */
25
+ .chzn-container-single .chzn-single {
26
+ background-color: #ffffff;
27
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 );
28
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
29
+ background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
30
+ background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
31
+ background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
32
+ background-image: -ms-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
33
+ background-image: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
34
+ -webkit-border-radius: 5px;
35
+ -moz-border-radius : 5px;
36
+ border-radius : 5px;
37
+ -moz-background-clip : padding;
38
+ -webkit-background-clip: padding-box;
39
+ background-clip : padding-box;
40
+ border: 1px solid #aaaaaa;
41
+ -webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
42
+ -moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
43
+ box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
44
+ display: block;
45
+ overflow: hidden;
46
+ white-space: nowrap;
47
+ position: relative;
48
+ height: 23px;
49
+ line-height: 24px;
50
+ padding: 0 0 0 8px;
51
+ color: #444444;
52
+ text-decoration: none;
53
+ }
54
+ .chzn-container-single .chzn-default {
55
+ color: #999;
56
+ }
57
+ .chzn-container-single .chzn-single span {
58
+ margin-right: 26px;
59
+ display: block;
60
+ overflow: hidden;
61
+ white-space: nowrap;
62
+ -o-text-overflow: ellipsis;
63
+ -ms-text-overflow: ellipsis;
64
+ text-overflow: ellipsis;
65
+ }
66
+ .chzn-container-single .chzn-single abbr {
67
+ display: block;
68
+ position: absolute;
69
+ right: 26px;
70
+ top: 6px;
71
+ width: 12px;
72
+ height: 13px;
73
+ font-size: 1px;
74
+ background: url('chosen-sprite.png') right top no-repeat;
75
+ }
76
+ .chzn-container-single .chzn-single abbr:hover {
77
+ background-position: right -11px;
78
+ }
79
+ .chzn-container-single.chzn-disabled .chzn-single abbr:hover {
80
+ background-position: right top;
81
+ }
82
+ .chzn-container-single .chzn-single div {
83
+ position: absolute;
84
+ right: 0;
85
+ top: 0;
86
+ display: block;
87
+ height: 100%;
88
+ width: 18px;
89
+ }
90
+ .chzn-container-single .chzn-single div b {
91
+ background: url('chosen-sprite.png') no-repeat 0 0;
92
+ display: block;
93
+ width: 100%;
94
+ height: 100%;
95
+ }
96
+ .chzn-container-single .chzn-search {
97
+ padding: 3px 4px;
98
+ position: relative;
99
+ margin: 0;
100
+ white-space: nowrap;
101
+ z-index: 1010;
102
+ }
103
+ .chzn-container-single .chzn-search input {
104
+ background: #fff url('chosen-sprite.png') no-repeat 100% -22px;
105
+ background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
106
+ background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
107
+ background: url('chosen-sprite.png') no-repeat 100% -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
108
+ background: url('chosen-sprite.png') no-repeat 100% -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
109
+ background: url('chosen-sprite.png') no-repeat 100% -22px, -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
110
+ background: url('chosen-sprite.png') no-repeat 100% -22px, linear-gradient(top, #eeeeee 1%, #ffffff 15%);
111
+ margin: 1px 0;
112
+ padding: 4px 20px 4px 5px;
113
+ outline: 0;
114
+ border: 1px solid #aaa;
115
+ font-family: sans-serif;
116
+ font-size: 1em;
117
+ }
118
+ .chzn-container-single .chzn-drop {
119
+ -webkit-border-radius: 0 0 4px 4px;
120
+ -moz-border-radius : 0 0 4px 4px;
121
+ border-radius : 0 0 4px 4px;
122
+ -moz-background-clip : padding;
123
+ -webkit-background-clip: padding-box;
124
+ background-clip : padding-box;
125
+ }
126
+ /* @end */
127
+
128
+ .chzn-container-single-nosearch .chzn-search input {
129
+ position: absolute;
130
+ left: -9000px;
131
+ }
132
+
133
+ /* @group Multi Chosen */
134
+ .chzn-container-multi .chzn-choices {
135
+ background-color: #fff;
136
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
137
+ background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
138
+ background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
139
+ background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
140
+ background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
141
+ background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
142
+ border: 1px solid #aaa;
143
+ margin: 0;
144
+ padding: 0;
145
+ cursor: text;
146
+ overflow: hidden;
147
+ height: auto !important;
148
+ height: 1%;
149
+ position: relative;
150
+ }
151
+ .chzn-container-multi .chzn-choices li {
152
+ float: left;
153
+ list-style: none;
154
+ }
155
+ .chzn-container-multi .chzn-choices .search-field {
156
+ white-space: nowrap;
157
+ margin: 0;
158
+ padding: 0;
159
+ }
160
+ .chzn-container-multi .chzn-choices .search-field input {
161
+ color: #666;
162
+ background: transparent !important;
163
+ border: 0 !important;
164
+ font-family: sans-serif;
165
+ font-size: 100%;
166
+ height: 15px;
167
+ padding: 5px;
168
+ margin: 1px 0;
169
+ outline: 0;
170
+ -webkit-box-shadow: none;
171
+ -moz-box-shadow : none;
172
+ -o-box-shadow : none;
173
+ box-shadow : none;
174
+ }
175
+ .chzn-container-multi .chzn-choices .search-field .default {
176
+ color: #999;
177
+ }
178
+ .chzn-container-multi .chzn-choices .search-choice {
179
+ -webkit-border-radius: 3px;
180
+ -moz-border-radius : 3px;
181
+ border-radius : 3px;
182
+ -moz-background-clip : padding;
183
+ -webkit-background-clip: padding-box;
184
+ background-clip : padding-box;
185
+ background-color: #e4e4e4;
186
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
187
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
188
+ background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
189
+ background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
190
+ background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
191
+ background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
192
+ background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
193
+ -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
194
+ -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
195
+ box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
196
+ color: #333;
197
+ border: 1px solid #aaaaaa;
198
+ line-height: 13px;
199
+ padding: 3px 20px 3px 5px;
200
+ margin: 3px 0 3px 5px;
201
+ position: relative;
202
+ cursor: default;
203
+ }
204
+ .chzn-container-multi .chzn-choices .search-choice-focus {
205
+ background: #d4d4d4;
206
+ }
207
+ .chzn-container-multi .chzn-choices .search-choice .search-choice-close {
208
+ display: block;
209
+ position: absolute;
210
+ right: 3px;
211
+ top: 4px;
212
+ width: 12px;
213
+ height: 13px;
214
+ font-size: 1px;
215
+ background: url('chosen-sprite.png') right top no-repeat;
216
+ }
217
+ .chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover {
218
+ background-position: right -11px;
219
+ }
220
+ .chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close {
221
+ background-position: right -11px;
222
+ }
223
+ /* @end */
224
+
225
+ /* @group Results */
226
+ .chzn-container .chzn-results {
227
+ margin: 0 4px 4px 0;
228
+ max-height: 240px;
229
+ padding: 0 0 0 4px;
230
+ position: relative;
231
+ overflow-x: hidden;
232
+ overflow-y: auto;
233
+ -webkit-overflow-scrolling: touch;
234
+ }
235
+ .chzn-container-multi .chzn-results {
236
+ margin: -1px 0 0;
237
+ padding: 0;
238
+ }
239
+ .chzn-container .chzn-results li {
240
+ display: none;
241
+ line-height: 15px;
242
+ padding: 5px 6px;
243
+ margin: 0;
244
+ list-style: none;
245
+ }
246
+ .chzn-container .chzn-results .active-result {
247
+ cursor: pointer;
248
+ display: list-item;
249
+ }
250
+ .chzn-container .chzn-results .highlighted {
251
+ background-color: #3875d7;
252
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 );
253
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
254
+ background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
255
+ background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
256
+ background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
257
+ background-image: -ms-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
258
+ background-image: linear-gradient(top, #3875d7 20%, #2a62bc 90%);
259
+ color: #fff;
260
+ }
261
+ .chzn-container .chzn-results li em {
262
+ background: #feffde;
263
+ font-style: normal;
264
+ }
265
+ .chzn-container .chzn-results .highlighted em {
266
+ background: transparent;
267
+ }
268
+ .chzn-container .chzn-results .no-results {
269
+ background: #f4f4f4;
270
+ display: list-item;
271
+ }
272
+ .chzn-container .chzn-results .group-result {
273
+ cursor: default;
274
+ color: #999;
275
+ font-weight: bold;
276
+ }
277
+ .chzn-container .chzn-results .group-option {
278
+ padding-left: 15px;
279
+ }
280
+ .chzn-container-multi .chzn-drop .result-selected {
281
+ display: none;
282
+ }
283
+ .chzn-container .chzn-results-scroll {
284
+ background: white;
285
+ margin: 0 4px;
286
+ position: absolute;
287
+ text-align: center;
288
+ width: 321px; /* This should by dynamic with js */
289
+ z-index: 1;
290
+ }
291
+ .chzn-container .chzn-results-scroll span {
292
+ display: inline-block;
293
+ height: 17px;
294
+ text-indent: -5000px;
295
+ width: 9px;
296
+ }
297
+ .chzn-container .chzn-results-scroll-down {
298
+ bottom: 0;
299
+ }
300
+ .chzn-container .chzn-results-scroll-down span {
301
+ background: url('chosen-sprite.png') no-repeat -4px -3px;
302
+ }
303
+ .chzn-container .chzn-results-scroll-up span {
304
+ background: url('chosen-sprite.png') no-repeat -22px -3px;
305
+ }
306
+ /* @end */
307
+
308
+ /* @group Active */
309
+ .chzn-container-active .chzn-single {
310
+ -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
311
+ -moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
312
+ -o-box-shadow : 0 0 5px rgba(0,0,0,.3);
313
+ box-shadow : 0 0 5px rgba(0,0,0,.3);
314
+ border: 1px solid #5897fb;
315
+ }
316
+ .chzn-container-active .chzn-single-with-drop {
317
+ border: 1px solid #aaa;
318
+ -webkit-box-shadow: 0 1px 0 #fff inset;
319
+ -moz-box-shadow : 0 1px 0 #fff inset;
320
+ -o-box-shadow : 0 1px 0 #fff inset;
321
+ box-shadow : 0 1px 0 #fff inset;
322
+ background-color: #eee;
323
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 );
324
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
325
+ background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
326
+ background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
327
+ background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
328
+ background-image: -ms-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
329
+ background-image: linear-gradient(top, #eeeeee 20%, #ffffff 80%);
330
+ -webkit-border-bottom-left-radius : 0;
331
+ -webkit-border-bottom-right-radius: 0;
332
+ -moz-border-radius-bottomleft : 0;
333
+ -moz-border-radius-bottomright: 0;
334
+ border-bottom-left-radius : 0;
335
+ border-bottom-right-radius: 0;
336
+ }
337
+ .chzn-container-active .chzn-single-with-drop div {
338
+ background: transparent;
339
+ border-left: none;
340
+ }
341
+ .chzn-container-active .chzn-single-with-drop div b {
342
+ background-position: -18px 1px;
343
+ }
344
+ .chzn-container-active .chzn-choices {
345
+ -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
346
+ -moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
347
+ -o-box-shadow : 0 0 5px rgba(0,0,0,.3);
348
+ box-shadow : 0 0 5px rgba(0,0,0,.3);
349
+ border: 1px solid #5897fb;
350
+ }
351
+ .chzn-container-active .chzn-choices .search-field input {
352
+ color: #111 !important;
353
+ }
354
+ /* @end */
355
+
356
+ /* @group Disabled Support */
357
+ .chzn-disabled {
358
+ cursor: default;
359
+ opacity:0.5 !important;
360
+ }
361
+ .chzn-disabled .chzn-single {
362
+ cursor: default;
363
+ }
364
+ .chzn-disabled .chzn-choices .search-choice .search-choice-close {
365
+ cursor: default;
366
+ }
367
+
368
+ /* @group Right to Left */
369
+ .chzn-rtl { text-align: right; }
370
+ .chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; }
371
+ .chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; }
372
+
373
+ .chzn-rtl .chzn-single div { left: 3px; right: auto; }
374
+ .chzn-rtl .chzn-single abbr {
375
+ left: 26px;
376
+ right: auto;
377
+ }
378
+ .chzn-rtl .chzn-choices .search-field input { direction: rtl; }
379
+ .chzn-rtl .chzn-choices li { float: right; }
380
+ .chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; }
381
+ .chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; background-position: right top;}
382
+ .chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; }
383
+ .chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; }
384
+ .chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; }
385
+ .chzn-rtl .chzn-search input {
386
+ background: #fff url('chosen-sprite.png') no-repeat -38px -22px;
387
+ background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
388
+ background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
389
+ background: url('chosen-sprite.png') no-repeat -38px -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
390
+ background: url('chosen-sprite.png') no-repeat -38px -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
391
+ background: url('chosen-sprite.png') no-repeat -38px -22px, -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
392
+ background: url('chosen-sprite.png') no-repeat -38px -22px, linear-gradient(top, #eeeeee 1%, #ffffff 15%);
393
+ padding: 4px 5px 4px 20px;
394
+ direction: rtl;
395
+ }
396
+ /* @end */
skin/adminhtml/default/default/wisepricer/chosen.proto.js ADDED
@@ -0,0 +1,1007 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Chosen, a Select Box Enhancer for jQuery and Protoype
2
+ // by Patrick Filler for Harvest, http://getharvest.com
3
+ //
4
+ // Version 0.9.8
5
+ // Full source at https://github.com/harvesthq/chosen
6
+ // Copyright (c) 2011 Harvest http://getharvest.com
7
+
8
+ // MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
9
+ // This file is generated by `cake build`, do not edit it by hand.
10
+ (function() {
11
+ var SelectParser;
12
+
13
+ SelectParser = (function() {
14
+
15
+ function SelectParser() {
16
+ this.options_index = 0;
17
+ this.parsed = [];
18
+ }
19
+
20
+ SelectParser.prototype.add_node = function(child) {
21
+ if (child.nodeName === "OPTGROUP") {
22
+ return this.add_group(child);
23
+ } else {
24
+ return this.add_option(child);
25
+ }
26
+ };
27
+
28
+ SelectParser.prototype.add_group = function(group) {
29
+ var group_position, option, _i, _len, _ref, _results;
30
+ group_position = this.parsed.length;
31
+ this.parsed.push({
32
+ array_index: group_position,
33
+ group: true,
34
+ label: group.label,
35
+ children: 0,
36
+ disabled: group.disabled
37
+ });
38
+ _ref = group.childNodes;
39
+ _results = [];
40
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
41
+ option = _ref[_i];
42
+ _results.push(this.add_option(option, group_position, group.disabled));
43
+ }
44
+ return _results;
45
+ };
46
+
47
+ SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
48
+ if (option.nodeName === "OPTION") {
49
+ if (option.text !== "") {
50
+ if (group_position != null) this.parsed[group_position].children += 1;
51
+ this.parsed.push({
52
+ array_index: this.parsed.length,
53
+ options_index: this.options_index,
54
+ value: option.value,
55
+ text: option.text,
56
+ html: option.innerHTML,
57
+ selected: option.selected,
58
+ disabled: group_disabled === true ? group_disabled : option.disabled,
59
+ group_array_index: group_position,
60
+ classes: option.className,
61
+ style: option.style.cssText
62
+ });
63
+ } else {
64
+ this.parsed.push({
65
+ array_index: this.parsed.length,
66
+ options_index: this.options_index,
67
+ empty: true
68
+ });
69
+ }
70
+ return this.options_index += 1;
71
+ }
72
+ };
73
+
74
+ return SelectParser;
75
+
76
+ })();
77
+
78
+ SelectParser.select_to_array = function(select) {
79
+ var child, parser, _i, _len, _ref;
80
+ parser = new SelectParser();
81
+ _ref = select.childNodes;
82
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
83
+ child = _ref[_i];
84
+ parser.add_node(child);
85
+ }
86
+ return parser.parsed;
87
+ };
88
+
89
+ this.SelectParser = SelectParser;
90
+
91
+ }).call(this);
92
+
93
+ /*
94
+ Chosen source: generate output using 'cake build'
95
+ Copyright (c) 2011 by Harvest
96
+ */
97
+
98
+ (function() {
99
+ var AbstractChosen, root;
100
+
101
+ root = this;
102
+
103
+ AbstractChosen = (function() {
104
+
105
+ function AbstractChosen(form_field, options) {
106
+ this.form_field = form_field;
107
+ this.options = options != null ? options : {};
108
+ this.set_default_values();
109
+ this.is_multiple = this.form_field.multiple;
110
+ this.set_default_text();
111
+ this.setup();
112
+ this.set_up_html();
113
+ this.register_observers();
114
+ this.finish_setup();
115
+ }
116
+
117
+ AbstractChosen.prototype.set_default_values = function() {
118
+ var _this = this;
119
+ this.click_test_action = function(evt) {
120
+ return _this.test_active_click(evt);
121
+ };
122
+ this.activate_action = function(evt) {
123
+ return _this.activate_field(evt);
124
+ };
125
+ this.active_field = false;
126
+ this.mouse_on_container = false;
127
+ this.results_showing = false;
128
+ this.result_highlighted = null;
129
+ this.result_single_selected = null;
130
+ this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
131
+ this.disable_search_threshold = this.options.disable_search_threshold || 0;
132
+ this.search_contains = this.options.search_contains || false;
133
+ this.choices = 0;
134
+ this.single_backstroke_delete = this.options.single_backstroke_delete || false;
135
+ return this.max_selected_options = this.options.max_selected_options || Infinity;
136
+ };
137
+
138
+ AbstractChosen.prototype.set_default_text = function() {
139
+ if (this.form_field.getAttribute("data-placeholder")) {
140
+ this.default_text = this.form_field.getAttribute("data-placeholder");
141
+ } else if (this.is_multiple) {
142
+ this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || "Select Some Options";
143
+ } else {
144
+ this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || "Select an Option";
145
+ }
146
+ return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || "No results match";
147
+ };
148
+
149
+ AbstractChosen.prototype.mouse_enter = function() {
150
+ return this.mouse_on_container = true;
151
+ };
152
+
153
+ AbstractChosen.prototype.mouse_leave = function() {
154
+ return this.mouse_on_container = false;
155
+ };
156
+
157
+ AbstractChosen.prototype.input_focus = function(evt) {
158
+ var _this = this;
159
+ if (!this.active_field) {
160
+ return setTimeout((function() {
161
+ return _this.container_mousedown();
162
+ }), 50);
163
+ }
164
+ };
165
+
166
+ AbstractChosen.prototype.input_blur = function(evt) {
167
+ var _this = this;
168
+ if (!this.mouse_on_container) {
169
+ this.active_field = false;
170
+ return setTimeout((function() {
171
+ return _this.blur_test();
172
+ }), 100);
173
+ }
174
+ };
175
+
176
+ AbstractChosen.prototype.result_add_option = function(option) {
177
+ var classes, style;
178
+ if (!option.disabled) {
179
+ option.dom_id = this.container_id + "_o_" + option.array_index;
180
+ classes = option.selected && this.is_multiple ? [] : ["active-result"];
181
+ if (option.selected) classes.push("result-selected");
182
+ if (option.group_array_index != null) classes.push("group-option");
183
+ if (option.classes !== "") classes.push(option.classes);
184
+ style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : "";
185
+ return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>';
186
+ } else {
187
+ return "";
188
+ }
189
+ };
190
+
191
+ AbstractChosen.prototype.results_update_field = function() {
192
+ if (!this.is_multiple) this.results_reset_cleanup();
193
+ this.result_clear_highlight();
194
+ this.result_single_selected = null;
195
+ return this.results_build();
196
+ };
197
+
198
+ AbstractChosen.prototype.results_toggle = function() {
199
+ if (this.results_showing) {
200
+ return this.results_hide();
201
+ } else {
202
+ return this.results_show();
203
+ }
204
+ };
205
+
206
+ AbstractChosen.prototype.results_search = function(evt) {
207
+ if (this.results_showing) {
208
+ return this.winnow_results();
209
+ } else {
210
+ return this.results_show();
211
+ }
212
+ };
213
+
214
+ AbstractChosen.prototype.keyup_checker = function(evt) {
215
+ var stroke, _ref;
216
+ stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
217
+ this.search_field_scale();
218
+ switch (stroke) {
219
+ case 8:
220
+ if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0) {
221
+ return this.keydown_backstroke();
222
+ } else if (!this.pending_backstroke) {
223
+ this.result_clear_highlight();
224
+ return this.results_search();
225
+ }
226
+ break;
227
+ case 13:
228
+ evt.preventDefault();
229
+ if (this.results_showing) return this.result_select(evt);
230
+ break;
231
+ case 27:
232
+ if (this.results_showing) this.results_hide();
233
+ return true;
234
+ case 9:
235
+ case 38:
236
+ case 40:
237
+ case 16:
238
+ case 91:
239
+ case 17:
240
+ break;
241
+ default:
242
+ return this.results_search();
243
+ }
244
+ };
245
+
246
+ AbstractChosen.prototype.generate_field_id = function() {
247
+ var new_id;
248
+ new_id = this.generate_random_id();
249
+ this.form_field.id = new_id;
250
+ return new_id;
251
+ };
252
+
253
+ AbstractChosen.prototype.generate_random_char = function() {
254
+ var chars, newchar, rand;
255
+ chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
256
+ rand = Math.floor(Math.random() * chars.length);
257
+ return newchar = chars.substring(rand, rand + 1);
258
+ };
259
+
260
+ return AbstractChosen;
261
+
262
+ })();
263
+
264
+ root.AbstractChosen = AbstractChosen;
265
+
266
+ }).call(this);
267
+
268
+ /*
269
+ Chosen source: generate output using 'cake build'
270
+ Copyright (c) 2011 by Harvest
271
+ */
272
+
273
+ (function() {
274
+ var Chosen, get_side_border_padding, root,
275
+ __hasProp = Object.prototype.hasOwnProperty,
276
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
277
+
278
+ root = this;
279
+
280
+ Chosen = (function(_super) {
281
+
282
+ __extends(Chosen, _super);
283
+
284
+ function Chosen() {
285
+ Chosen.__super__.constructor.apply(this, arguments);
286
+ }
287
+
288
+ Chosen.prototype.setup = function() {
289
+ this.current_value = this.form_field.value;
290
+ return this.is_rtl = this.form_field.hasClassName("chzn-rtl");
291
+ };
292
+
293
+ Chosen.prototype.finish_setup = function() {
294
+ return this.form_field.addClassName("chzn-done");
295
+ };
296
+
297
+ Chosen.prototype.set_default_values = function() {
298
+ Chosen.__super__.set_default_values.call(this);
299
+ this.single_temp = new Template('<a href="javascript:void(0)" class="chzn-single chzn-default"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>');
300
+ this.multi_temp = new Template('<ul class="chzn-choices"><li class="search-field"><input type="text" value="#{default}" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>');
301
+ this.choice_temp = new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>');
302
+ return this.no_results_temp = new Template('<li class="no-results">' + this.results_none_found + ' "<span>#{terms}</span>"</li>');
303
+ };
304
+
305
+ Chosen.prototype.set_up_html = function() {
306
+ var base_template, container_props, dd_top, dd_width, sf_width;
307
+ this.container_id = this.form_field.identify().replace(/[^\w]/g, '_') + "_chzn";
308
+ this.f_width = this.form_field.getStyle("width") ? parseInt(this.form_field.getStyle("width"), 10) : this.form_field.getWidth();
309
+ container_props = {
310
+ 'id': this.container_id,
311
+ 'class': "chzn-container" + (this.is_rtl ? ' chzn-rtl' : ''),
312
+ 'style': 'width: ' + this.f_width + 'px'
313
+ };
314
+ base_template = this.is_multiple ? new Element('div', container_props).update(this.multi_temp.evaluate({
315
+ "default": this.default_text
316
+ })) : new Element('div', container_props).update(this.single_temp.evaluate({
317
+ "default": this.default_text
318
+ }));
319
+ this.form_field.hide().insert({
320
+ after: base_template
321
+ });
322
+ this.container = $(this.container_id);
323
+ this.container.addClassName("chzn-container-" + (this.is_multiple ? "multi" : "single"));
324
+ this.dropdown = this.container.down('div.chzn-drop');
325
+ dd_top = this.container.getHeight();
326
+ dd_width = this.f_width - get_side_border_padding(this.dropdown);
327
+ this.dropdown.setStyle({
328
+ "width": dd_width + "px",
329
+ "top": dd_top + "px"
330
+ });
331
+ this.search_field = this.container.down('input');
332
+ this.search_results = this.container.down('ul.chzn-results');
333
+ this.search_field_scale();
334
+ this.search_no_results = this.container.down('li.no-results');
335
+ if (this.is_multiple) {
336
+ this.search_choices = this.container.down('ul.chzn-choices');
337
+ this.search_container = this.container.down('li.search-field');
338
+ } else {
339
+ this.search_container = this.container.down('div.chzn-search');
340
+ this.selected_item = this.container.down('.chzn-single');
341
+ sf_width = dd_width - get_side_border_padding(this.search_container) - get_side_border_padding(this.search_field);
342
+ this.search_field.setStyle({
343
+ "width": sf_width + "px"
344
+ });
345
+ }
346
+ this.results_build();
347
+ this.set_tab_index();
348
+ return this.form_field.fire("liszt:ready", {
349
+ chosen: this
350
+ });
351
+ };
352
+
353
+ Chosen.prototype.register_observers = function() {
354
+ var _this = this;
355
+ this.container.observe("mousedown", function(evt) {
356
+ return _this.container_mousedown(evt);
357
+ });
358
+ this.container.observe("mouseup", function(evt) {
359
+ return _this.container_mouseup(evt);
360
+ });
361
+ this.container.observe("mouseenter", function(evt) {
362
+ return _this.mouse_enter(evt);
363
+ });
364
+ this.container.observe("mouseleave", function(evt) {
365
+ return _this.mouse_leave(evt);
366
+ });
367
+ this.search_results.observe("mouseup", function(evt) {
368
+ return _this.search_results_mouseup(evt);
369
+ });
370
+ this.search_results.observe("mouseover", function(evt) {
371
+ return _this.search_results_mouseover(evt);
372
+ });
373
+ this.search_results.observe("mouseout", function(evt) {
374
+ return _this.search_results_mouseout(evt);
375
+ });
376
+ this.form_field.observe("liszt:updated", function(evt) {
377
+ return _this.results_update_field(evt);
378
+ });
379
+ this.search_field.observe("blur", function(evt) {
380
+ return _this.input_blur(evt);
381
+ });
382
+ this.search_field.observe("keyup", function(evt) {
383
+ return _this.keyup_checker(evt);
384
+ });
385
+ this.search_field.observe("keydown", function(evt) {
386
+ return _this.keydown_checker(evt);
387
+ });
388
+ if (this.is_multiple) {
389
+ this.search_choices.observe("click", function(evt) {
390
+ return _this.choices_click(evt);
391
+ });
392
+ return this.search_field.observe("focus", function(evt) {
393
+ return _this.input_focus(evt);
394
+ });
395
+ } else {
396
+ return this.container.observe("click", function(evt) {
397
+ return evt.preventDefault();
398
+ });
399
+ }
400
+ };
401
+
402
+ Chosen.prototype.search_field_disabled = function() {
403
+ this.is_disabled = this.form_field.disabled;
404
+ if (this.is_disabled) {
405
+ this.container.addClassName('chzn-disabled');
406
+ this.search_field.disabled = true;
407
+ if (!this.is_multiple) {
408
+ this.selected_item.stopObserving("focus", this.activate_action);
409
+ }
410
+ return this.close_field();
411
+ } else {
412
+ this.container.removeClassName('chzn-disabled');
413
+ this.search_field.disabled = false;
414
+ if (!this.is_multiple) {
415
+ return this.selected_item.observe("focus", this.activate_action);
416
+ }
417
+ }
418
+ };
419
+
420
+ Chosen.prototype.container_mousedown = function(evt) {
421
+ var target_closelink;
422
+ if (!this.is_disabled) {
423
+ target_closelink = evt != null ? evt.target.hasClassName("search-choice-close") : false;
424
+ if (evt && evt.type === "mousedown" && !this.results_showing) evt.stop();
425
+ if (!this.pending_destroy_click && !target_closelink) {
426
+ if (!this.active_field) {
427
+ if (this.is_multiple) this.search_field.clear();
428
+ document.observe("click", this.click_test_action);
429
+ this.results_show();
430
+ } else if (!this.is_multiple && evt && (evt.target === this.selected_item || evt.target.up("a.chzn-single"))) {
431
+ this.results_toggle();
432
+ }
433
+ return this.activate_field();
434
+ } else {
435
+ return this.pending_destroy_click = false;
436
+ }
437
+ }
438
+ };
439
+
440
+ Chosen.prototype.container_mouseup = function(evt) {
441
+ if (evt.target.nodeName === "ABBR" && !this.is_disabled) {
442
+ return this.results_reset(evt);
443
+ }
444
+ };
445
+
446
+ Chosen.prototype.blur_test = function(evt) {
447
+ if (!this.active_field && this.container.hasClassName("chzn-container-active")) {
448
+ return this.close_field();
449
+ }
450
+ };
451
+
452
+ Chosen.prototype.close_field = function() {
453
+ document.stopObserving("click", this.click_test_action);
454
+ if (!this.is_multiple) {
455
+ this.selected_item.tabIndex = this.search_field.tabIndex;
456
+ this.search_field.tabIndex = -1;
457
+ }
458
+ this.active_field = false;
459
+ this.results_hide();
460
+ this.container.removeClassName("chzn-container-active");
461
+ this.winnow_results_clear();
462
+ this.clear_backstroke();
463
+ this.show_search_field_default();
464
+ return this.search_field_scale();
465
+ };
466
+
467
+ Chosen.prototype.activate_field = function() {
468
+ if (!this.is_multiple && !this.active_field) {
469
+ this.search_field.tabIndex = this.selected_item.tabIndex;
470
+ this.selected_item.tabIndex = -1;
471
+ }
472
+ this.container.addClassName("chzn-container-active");
473
+ this.active_field = true;
474
+ this.search_field.value = this.search_field.value;
475
+ return this.search_field.focus();
476
+ };
477
+
478
+ Chosen.prototype.test_active_click = function(evt) {
479
+ if (evt.target.up('#' + this.container_id)) {
480
+ return this.active_field = true;
481
+ } else {
482
+ return this.close_field();
483
+ }
484
+ };
485
+
486
+ Chosen.prototype.results_build = function() {
487
+ var content, data, _i, _len, _ref;
488
+ this.parsing = true;
489
+ this.results_data = root.SelectParser.select_to_array(this.form_field);
490
+ if (this.is_multiple && this.choices > 0) {
491
+ this.search_choices.select("li.search-choice").invoke("remove");
492
+ this.choices = 0;
493
+ } else if (!this.is_multiple) {
494
+ this.selected_item.addClassName("chzn-default").down("span").update(this.default_text);
495
+ if (this.form_field.options.length <= this.disable_search_threshold) {
496
+ this.container.addClassName("chzn-container-single-nosearch");
497
+ } else {
498
+ this.container.removeClassName("chzn-container-single-nosearch");
499
+ }
500
+ }
501
+ content = '';
502
+ _ref = this.results_data;
503
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
504
+ data = _ref[_i];
505
+ if (data.group) {
506
+ content += this.result_add_group(data);
507
+ } else if (!data.empty) {
508
+ content += this.result_add_option(data);
509
+ if (data.selected && this.is_multiple) {
510
+ this.choice_build(data);
511
+ } else if (data.selected && !this.is_multiple) {
512
+ this.selected_item.removeClassName("chzn-default").down("span").update(data.html);
513
+ if (this.allow_single_deselect) this.single_deselect_control_build();
514
+ }
515
+ }
516
+ }
517
+ this.search_field_disabled();
518
+ this.show_search_field_default();
519
+ this.search_field_scale();
520
+ this.search_results.update(content);
521
+ return this.parsing = false;
522
+ };
523
+
524
+ Chosen.prototype.result_add_group = function(group) {
525
+ if (!group.disabled) {
526
+ group.dom_id = this.container_id + "_g_" + group.array_index;
527
+ return '<li id="' + group.dom_id + '" class="group-result">' + group.label.escapeHTML() + '</li>';
528
+ } else {
529
+ return "";
530
+ }
531
+ };
532
+
533
+ Chosen.prototype.result_do_highlight = function(el) {
534
+ var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
535
+ this.result_clear_highlight();
536
+ this.result_highlight = el;
537
+ this.result_highlight.addClassName("highlighted");
538
+ maxHeight = parseInt(this.search_results.getStyle('maxHeight'), 10);
539
+ visible_top = this.search_results.scrollTop;
540
+ visible_bottom = maxHeight + visible_top;
541
+ high_top = this.result_highlight.positionedOffset().top;
542
+ high_bottom = high_top + this.result_highlight.getHeight();
543
+ if (high_bottom >= visible_bottom) {
544
+ return this.search_results.scrollTop = (high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0;
545
+ } else if (high_top < visible_top) {
546
+ return this.search_results.scrollTop = high_top;
547
+ }
548
+ };
549
+
550
+ Chosen.prototype.result_clear_highlight = function() {
551
+ if (this.result_highlight) {
552
+ this.result_highlight.removeClassName('highlighted');
553
+ }
554
+ return this.result_highlight = null;
555
+ };
556
+
557
+ Chosen.prototype.results_show = function() {
558
+ var dd_top;
559
+ if (!this.is_multiple) {
560
+ this.selected_item.addClassName('chzn-single-with-drop');
561
+ if (this.result_single_selected) {
562
+ this.result_do_highlight(this.result_single_selected);
563
+ }
564
+ } else if (this.max_selected_options <= this.choices) {
565
+ this.form_field.fire("liszt:maxselected", {
566
+ chosen: this
567
+ });
568
+ return false;
569
+ }
570
+ dd_top = this.is_multiple ? this.container.getHeight() : this.container.getHeight() - 1;
571
+ this.form_field.fire("liszt:showing_dropdown", {
572
+ chosen: this
573
+ });
574
+ this.dropdown.setStyle({
575
+ "top": dd_top + "px",
576
+ "left": 0
577
+ });
578
+ this.results_showing = true;
579
+ this.search_field.focus();
580
+ this.search_field.value = this.search_field.value;
581
+ return this.winnow_results();
582
+ };
583
+
584
+ Chosen.prototype.results_hide = function() {
585
+ if (!this.is_multiple) {
586
+ this.selected_item.removeClassName('chzn-single-with-drop');
587
+ }
588
+ this.result_clear_highlight();
589
+ this.form_field.fire("liszt:hiding_dropdown", {
590
+ chosen: this
591
+ });
592
+ this.dropdown.setStyle({
593
+ "left": "-9000px"
594
+ });
595
+ return this.results_showing = false;
596
+ };
597
+
598
+ Chosen.prototype.set_tab_index = function(el) {
599
+ var ti;
600
+ if (this.form_field.tabIndex) {
601
+ ti = this.form_field.tabIndex;
602
+ this.form_field.tabIndex = -1;
603
+ if (this.is_multiple) {
604
+ return this.search_field.tabIndex = ti;
605
+ } else {
606
+ this.selected_item.tabIndex = ti;
607
+ return this.search_field.tabIndex = -1;
608
+ }
609
+ }
610
+ };
611
+
612
+ Chosen.prototype.show_search_field_default = function() {
613
+ if (this.is_multiple && this.choices < 1 && !this.active_field) {
614
+ this.search_field.value = this.default_text;
615
+ return this.search_field.addClassName("default");
616
+ } else {
617
+ this.search_field.value = "";
618
+ return this.search_field.removeClassName("default");
619
+ }
620
+ };
621
+
622
+ Chosen.prototype.search_results_mouseup = function(evt) {
623
+ var target;
624
+ target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result");
625
+ if (target) {
626
+ this.result_highlight = target;
627
+ return this.result_select(evt);
628
+ }
629
+ };
630
+
631
+ Chosen.prototype.search_results_mouseover = function(evt) {
632
+ var target;
633
+ target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result");
634
+ if (target) return this.result_do_highlight(target);
635
+ };
636
+
637
+ Chosen.prototype.search_results_mouseout = function(evt) {
638
+ if (evt.target.hasClassName('active-result') || evt.target.up('.active-result')) {
639
+ return this.result_clear_highlight();
640
+ }
641
+ };
642
+
643
+ Chosen.prototype.choices_click = function(evt) {
644
+ evt.preventDefault();
645
+ if (this.active_field && !(evt.target.hasClassName('search-choice') || evt.target.up('.search-choice')) && !this.results_showing) {
646
+ return this.results_show();
647
+ }
648
+ };
649
+
650
+ Chosen.prototype.choice_build = function(item) {
651
+ var choice_id, link,
652
+ _this = this;
653
+ if (this.is_multiple && this.max_selected_options <= this.choices) {
654
+ this.form_field.fire("liszt:maxselected", {
655
+ chosen: this
656
+ });
657
+ return false;
658
+ }
659
+ choice_id = this.container_id + "_c_" + item.array_index;
660
+ this.choices += 1;
661
+ this.search_container.insert({
662
+ before: this.choice_temp.evaluate({
663
+ id: choice_id,
664
+ choice: item.html,
665
+ position: item.array_index
666
+ })
667
+ });
668
+ link = $(choice_id).down('a');
669
+ return link.observe("click", function(evt) {
670
+ return _this.choice_destroy_link_click(evt);
671
+ });
672
+ };
673
+
674
+ Chosen.prototype.choice_destroy_link_click = function(evt) {
675
+ evt.preventDefault();
676
+ if (!this.is_disabled) {
677
+ this.pending_destroy_click = true;
678
+ return this.choice_destroy(evt.target);
679
+ }
680
+ };
681
+
682
+ Chosen.prototype.choice_destroy = function(link) {
683
+ this.choices -= 1;
684
+ this.show_search_field_default();
685
+ if (this.is_multiple && this.choices > 0 && this.search_field.value.length < 1) {
686
+ this.results_hide();
687
+ }
688
+ this.result_deselect(link.readAttribute("rel"));
689
+ return link.up('li').remove();
690
+ };
691
+
692
+ Chosen.prototype.results_reset = function() {
693
+ this.form_field.options[0].selected = true;
694
+ this.selected_item.down("span").update(this.default_text);
695
+ if (!this.is_multiple) this.selected_item.addClassName("chzn-default");
696
+ this.show_search_field_default();
697
+ this.results_reset_cleanup();
698
+ if (typeof Event.simulate === 'function') this.form_field.simulate("change");
699
+ if (this.active_field) return this.results_hide();
700
+ };
701
+
702
+ Chosen.prototype.results_reset_cleanup = function() {
703
+ var deselect_trigger;
704
+ deselect_trigger = this.selected_item.down("abbr");
705
+ if (deselect_trigger) return deselect_trigger.remove();
706
+ };
707
+
708
+ Chosen.prototype.result_select = function(evt) {
709
+ var high, item, position;
710
+ if (this.result_highlight) {
711
+ high = this.result_highlight;
712
+ this.result_clear_highlight();
713
+ if (this.is_multiple) {
714
+ this.result_deactivate(high);
715
+ } else {
716
+ this.search_results.descendants(".result-selected").invoke("removeClassName", "result-selected");
717
+ this.selected_item.removeClassName("chzn-default");
718
+ this.result_single_selected = high;
719
+ }
720
+ high.addClassName("result-selected");
721
+ position = high.id.substr(high.id.lastIndexOf("_") + 1);
722
+ item = this.results_data[position];
723
+ item.selected = true;
724
+ this.form_field.options[item.options_index].selected = true;
725
+ if (this.is_multiple) {
726
+ this.choice_build(item);
727
+ } else {
728
+ this.selected_item.down("span").update(item.html);
729
+ if (this.allow_single_deselect) this.single_deselect_control_build();
730
+ }
731
+ if (!(evt.metaKey && this.is_multiple)) this.results_hide();
732
+ this.search_field.value = "";
733
+ if (typeof Event.simulate === 'function' && (this.is_multiple || this.form_field.value !== this.current_value)) {
734
+ this.form_field.simulate("change");
735
+ }
736
+ this.current_value = this.form_field.value;
737
+ return this.search_field_scale();
738
+ }
739
+ };
740
+
741
+ Chosen.prototype.result_activate = function(el) {
742
+ return el.addClassName("active-result");
743
+ };
744
+
745
+ Chosen.prototype.result_deactivate = function(el) {
746
+ return el.removeClassName("active-result");
747
+ };
748
+
749
+ Chosen.prototype.result_deselect = function(pos) {
750
+ var result, result_data;
751
+ result_data = this.results_data[pos];
752
+ result_data.selected = false;
753
+ this.form_field.options[result_data.options_index].selected = false;
754
+ result = $(this.container_id + "_o_" + pos);
755
+ result.removeClassName("result-selected").addClassName("active-result").show();
756
+ this.result_clear_highlight();
757
+ this.winnow_results();
758
+ if (typeof Event.simulate === 'function') this.form_field.simulate("change");
759
+ return this.search_field_scale();
760
+ };
761
+
762
+ Chosen.prototype.single_deselect_control_build = function() {
763
+ if (this.allow_single_deselect && !this.selected_item.down("abbr")) {
764
+ return this.selected_item.down("span").insert({
765
+ after: "<abbr class=\"search-choice-close\"></abbr>"
766
+ });
767
+ }
768
+ };
769
+
770
+ Chosen.prototype.winnow_results = function() {
771
+ var found, option, part, parts, regex, regexAnchor, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len2, _ref;
772
+ this.no_results_clear();
773
+ results = 0;
774
+ searchText = this.search_field.value === this.default_text ? "" : this.search_field.value.strip().escapeHTML();
775
+ regexAnchor = this.search_contains ? "" : "^";
776
+ regex = new RegExp(regexAnchor + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
777
+ zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
778
+ _ref = this.results_data;
779
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
780
+ option = _ref[_i];
781
+ if (!option.disabled && !option.empty) {
782
+ if (option.group) {
783
+ $(option.dom_id).hide();
784
+ } else if (!(this.is_multiple && option.selected)) {
785
+ found = false;
786
+ result_id = option.dom_id;
787
+ if (regex.test(option.html)) {
788
+ found = true;
789
+ results += 1;
790
+ } else if (option.html.indexOf(" ") >= 0 || option.html.indexOf("[") === 0) {
791
+ parts = option.html.replace(/\[|\]/g, "").split(" ");
792
+ if (parts.length) {
793
+ for (_j = 0, _len2 = parts.length; _j < _len2; _j++) {
794
+ part = parts[_j];
795
+ if (regex.test(part)) {
796
+ found = true;
797
+ results += 1;
798
+ }
799
+ }
800
+ }
801
+ }
802
+ if (found) {
803
+ if (searchText.length) {
804
+ startpos = option.html.search(zregex);
805
+ text = option.html.substr(0, startpos + searchText.length) + '</em>' + option.html.substr(startpos + searchText.length);
806
+ text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
807
+ } else {
808
+ text = option.html;
809
+ }
810
+ if ($(result_id).innerHTML !== text) $(result_id).update(text);
811
+ this.result_activate($(result_id));
812
+ if (option.group_array_index != null) {
813
+ $(this.results_data[option.group_array_index].dom_id).setStyle({
814
+ display: 'list-item'
815
+ });
816
+ }
817
+ } else {
818
+ if ($(result_id) === this.result_highlight) {
819
+ this.result_clear_highlight();
820
+ }
821
+ this.result_deactivate($(result_id));
822
+ }
823
+ }
824
+ }
825
+ }
826
+ if (results < 1 && searchText.length) {
827
+ return this.no_results(searchText);
828
+ } else {
829
+ return this.winnow_results_set_highlight();
830
+ }
831
+ };
832
+
833
+ Chosen.prototype.winnow_results_clear = function() {
834
+ var li, lis, _i, _len, _results;
835
+ this.search_field.clear();
836
+ lis = this.search_results.select("li");
837
+ _results = [];
838
+ for (_i = 0, _len = lis.length; _i < _len; _i++) {
839
+ li = lis[_i];
840
+ if (li.hasClassName("group-result")) {
841
+ _results.push(li.show());
842
+ } else if (!this.is_multiple || !li.hasClassName("result-selected")) {
843
+ _results.push(this.result_activate(li));
844
+ } else {
845
+ _results.push(void 0);
846
+ }
847
+ }
848
+ return _results;
849
+ };
850
+
851
+ Chosen.prototype.winnow_results_set_highlight = function() {
852
+ var do_high;
853
+ if (!this.result_highlight) {
854
+ if (!this.is_multiple) {
855
+ do_high = this.search_results.down(".result-selected.active-result");
856
+ }
857
+ if (!(do_high != null)) {
858
+ do_high = this.search_results.down(".active-result");
859
+ }
860
+ if (do_high != null) return this.result_do_highlight(do_high);
861
+ }
862
+ };
863
+
864
+ Chosen.prototype.no_results = function(terms) {
865
+ return this.search_results.insert(this.no_results_temp.evaluate({
866
+ terms: terms
867
+ }));
868
+ };
869
+
870
+ Chosen.prototype.no_results_clear = function() {
871
+ var nr, _results;
872
+ nr = null;
873
+ _results = [];
874
+ while (nr = this.search_results.down(".no-results")) {
875
+ _results.push(nr.remove());
876
+ }
877
+ return _results;
878
+ };
879
+
880
+ Chosen.prototype.keydown_arrow = function() {
881
+ var actives, nexts, sibs;
882
+ actives = this.search_results.select("li.active-result");
883
+ if (actives.length) {
884
+ if (!this.result_highlight) {
885
+ this.result_do_highlight(actives.first());
886
+ } else if (this.results_showing) {
887
+ sibs = this.result_highlight.nextSiblings();
888
+ nexts = sibs.intersect(actives);
889
+ if (nexts.length) this.result_do_highlight(nexts.first());
890
+ }
891
+ if (!this.results_showing) return this.results_show();
892
+ }
893
+ };
894
+
895
+ Chosen.prototype.keyup_arrow = function() {
896
+ var actives, prevs, sibs;
897
+ if (!this.results_showing && !this.is_multiple) {
898
+ return this.results_show();
899
+ } else if (this.result_highlight) {
900
+ sibs = this.result_highlight.previousSiblings();
901
+ actives = this.search_results.select("li.active-result");
902
+ prevs = sibs.intersect(actives);
903
+ if (prevs.length) {
904
+ return this.result_do_highlight(prevs.first());
905
+ } else {
906
+ if (this.choices > 0) this.results_hide();
907
+ return this.result_clear_highlight();
908
+ }
909
+ }
910
+ };
911
+
912
+ Chosen.prototype.keydown_backstroke = function() {
913
+ if (this.pending_backstroke) {
914
+ this.choice_destroy(this.pending_backstroke.down("a"));
915
+ return this.clear_backstroke();
916
+ } else {
917
+ this.pending_backstroke = this.search_container.siblings("li.search-choice").last();
918
+ if (this.single_backstroke_delete) {
919
+ return this.keydown_backstroke();
920
+ } else {
921
+ return this.pending_backstroke.addClassName("search-choice-focus");
922
+ }
923
+ }
924
+ };
925
+
926
+ Chosen.prototype.clear_backstroke = function() {
927
+ if (this.pending_backstroke) {
928
+ this.pending_backstroke.removeClassName("search-choice-focus");
929
+ }
930
+ return this.pending_backstroke = null;
931
+ };
932
+
933
+ Chosen.prototype.keydown_checker = function(evt) {
934
+ var stroke, _ref;
935
+ stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
936
+ this.search_field_scale();
937
+ if (stroke !== 8 && this.pending_backstroke) this.clear_backstroke();
938
+ switch (stroke) {
939
+ case 8:
940
+ this.backstroke_length = this.search_field.value.length;
941
+ break;
942
+ case 9:
943
+ if (this.results_showing && !this.is_multiple) this.result_select(evt);
944
+ this.mouse_on_container = false;
945
+ break;
946
+ case 13:
947
+ evt.preventDefault();
948
+ break;
949
+ case 38:
950
+ evt.preventDefault();
951
+ this.keyup_arrow();
952
+ break;
953
+ case 40:
954
+ this.keydown_arrow();
955
+ break;
956
+ }
957
+ };
958
+
959
+ Chosen.prototype.search_field_scale = function() {
960
+ var dd_top, div, h, style, style_block, styles, w, _i, _len;
961
+ if (this.is_multiple) {
962
+ h = 0;
963
+ w = 0;
964
+ style_block = "position:absolute; left: -1000px; top: -1000px; display:none;";
965
+ styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'];
966
+ for (_i = 0, _len = styles.length; _i < _len; _i++) {
967
+ style = styles[_i];
968
+ style_block += style + ":" + this.search_field.getStyle(style) + ";";
969
+ }
970
+ div = new Element('div', {
971
+ 'style': style_block
972
+ }).update(this.search_field.value.escapeHTML());
973
+ document.body.appendChild(div);
974
+ w = Element.measure(div, 'width') + 25;
975
+ div.remove();
976
+ if (w > this.f_width - 10) w = this.f_width - 10;
977
+ this.search_field.setStyle({
978
+ 'width': w + 'px'
979
+ });
980
+ dd_top = this.container.getHeight();
981
+ return this.dropdown.setStyle({
982
+ "top": dd_top + "px"
983
+ });
984
+ }
985
+ };
986
+
987
+ return Chosen;
988
+
989
+ })(AbstractChosen);
990
+
991
+ root.Chosen = Chosen;
992
+
993
+ if (Prototype.Browser.IE) {
994
+ if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
995
+ Prototype.BrowserFeatures['Version'] = new Number(RegExp.$1);
996
+ }
997
+ }
998
+
999
+ get_side_border_padding = function(elmt) {
1000
+ var layout, side_border_padding;
1001
+ layout = new Element.Layout(elmt);
1002
+ return side_border_padding = layout.get("border-left") + layout.get("border-right") + layout.get("padding-left") + layout.get("padding-right");
1003
+ };
1004
+
1005
+ root.get_side_border_padding = get_side_border_padding;
1006
+
1007
+ }).call(this);
skin/adminhtml/default/default/wisepricer/myprototype.js ADDED
@@ -0,0 +1,6082 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Prototype JavaScript framework, version 1.7
2
+ * (c) 2005-2010 Sam Stephenson
3
+ *
4
+ * Prototype is freely distributable under the terms of an MIT-style license.
5
+ * For details, see the Prototype web site: http://www.prototypejs.org/
6
+ *
7
+ *--------------------------------------------------------------------------*/
8
+
9
+ var Prototype = {
10
+
11
+ Version: '1.7',
12
+
13
+ Browser: (function(){
14
+ var ua = navigator.userAgent;
15
+ var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
16
+ return {
17
+ IE: !!window.attachEvent && !isOpera,
18
+ Opera: isOpera,
19
+ WebKit: ua.indexOf('AppleWebKit/') > -1,
20
+ Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
21
+ MobileSafari: /Apple.*Mobile/.test(ua)
22
+ }
23
+ })(),
24
+
25
+ BrowserFeatures: {
26
+ XPath: !!document.evaluate,
27
+
28
+ SelectorsAPI: !!document.querySelector,
29
+
30
+ ElementExtensions: (function() {
31
+ var constructor = window.Element || window.HTMLElement;
32
+ return !!(constructor && constructor.prototype);
33
+ })(),
34
+ SpecificElementExtensions: (function() {
35
+ if (typeof window.HTMLDivElement !== 'undefined')
36
+ return true;
37
+
38
+ var div = document.createElement('div'),
39
+ form = document.createElement('form'),
40
+ isSupported = false;
41
+
42
+ if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
43
+ isSupported = true;
44
+ }
45
+
46
+ div = form = null;
47
+
48
+ return isSupported;
49
+ })()
50
+ },
51
+
52
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
53
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
54
+
55
+ emptyFunction: function() { },
56
+
57
+ K: function(x) { return x }
58
+ };
59
+
60
+ if (Prototype.Browser.MobileSafari)
61
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
62
+
63
+
64
+ var Abstract = { };
65
+
66
+
67
+ var Try = {
68
+ these: function() {
69
+ var returnValue;
70
+
71
+ for (var i = 0, length = arguments.length; i < length; i++) {
72
+ var lambda = arguments[i];
73
+ try {
74
+ returnValue = lambda();
75
+ break;
76
+ } catch (e) { }
77
+ }
78
+
79
+ return returnValue;
80
+ }
81
+ };
82
+
83
+ /* Based on Alex Arnell's inheritance implementation. */
84
+
85
+ var Class = (function() {
86
+
87
+ var IS_DONTENUM_BUGGY = (function(){
88
+ for (var p in { toString: 1 }) {
89
+ if (p === 'toString') return false;
90
+ }
91
+ return true;
92
+ })();
93
+
94
+ function subclass() {};
95
+ function create() {
96
+ var parent = null, properties = $A(arguments);
97
+ if (Object.isFunction(properties[0]))
98
+ parent = properties.shift();
99
+
100
+ function klass() {
101
+ this.initialize.apply(this, arguments);
102
+ }
103
+
104
+ Object.extend(klass, Class.Methods);
105
+ klass.superclass = parent;
106
+ klass.subclasses = [];
107
+
108
+ if (parent) {
109
+ subclass.prototype = parent.prototype;
110
+ klass.prototype = new subclass;
111
+ parent.subclasses.push(klass);
112
+ }
113
+
114
+ for (var i = 0, length = properties.length; i < length; i++)
115
+ klass.addMethods(properties[i]);
116
+
117
+ if (!klass.prototype.initialize)
118
+ klass.prototype.initialize = Prototype.emptyFunction;
119
+
120
+ klass.prototype.constructor = klass;
121
+ return klass;
122
+ }
123
+
124
+ function addMethods(source) {
125
+ var ancestor = this.superclass && this.superclass.prototype,
126
+ properties = Object.keys(source);
127
+
128
+ if (IS_DONTENUM_BUGGY) {
129
+ if (source.toString != Object.prototype.toString)
130
+ properties.push("toString");
131
+ if (source.valueOf != Object.prototype.valueOf)
132
+ properties.push("valueOf");
133
+ }
134
+
135
+ for (var i = 0, length = properties.length; i < length; i++) {
136
+ var property = properties[i], value = source[property];
137
+ if (ancestor && Object.isFunction(value) &&
138
+ value.argumentNames()[0] == "$super") {
139
+ var method = value;
140
+ value = (function(m) {
141
+ return function() { return ancestor[m].apply(this, arguments); };
142
+ })(property).wrap(method);
143
+
144
+ value.valueOf = method.valueOf.bind(method);
145
+ value.toString = method.toString.bind(method);
146
+ }
147
+ this.prototype[property] = value;
148
+ }
149
+
150
+ return this;
151
+ }
152
+
153
+ return {
154
+ create: create,
155
+ Methods: {
156
+ addMethods: addMethods
157
+ }
158
+ };
159
+ })();
160
+ (function() {
161
+
162
+ var _toString = Object.prototype.toString,
163
+ NULL_TYPE = 'Null',
164
+ UNDEFINED_TYPE = 'Undefined',
165
+ BOOLEAN_TYPE = 'Boolean',
166
+ NUMBER_TYPE = 'Number',
167
+ STRING_TYPE = 'String',
168
+ OBJECT_TYPE = 'Object',
169
+ FUNCTION_CLASS = '[object Function]',
170
+ BOOLEAN_CLASS = '[object Boolean]',
171
+ NUMBER_CLASS = '[object Number]',
172
+ STRING_CLASS = '[object String]',
173
+ ARRAY_CLASS = '[object Array]',
174
+ DATE_CLASS = '[object Date]',
175
+ NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
176
+ typeof JSON.stringify === 'function' &&
177
+ JSON.stringify(0) === '0' &&
178
+ typeof JSON.stringify(Prototype.K) === 'undefined';
179
+
180
+ function Type(o) {
181
+ switch(o) {
182
+ case null: return NULL_TYPE;
183
+ case (void 0): return UNDEFINED_TYPE;
184
+ }
185
+ var type = typeof o;
186
+ switch(type) {
187
+ case 'boolean': return BOOLEAN_TYPE;
188
+ case 'number': return NUMBER_TYPE;
189
+ case 'string': return STRING_TYPE;
190
+ }
191
+ return OBJECT_TYPE;
192
+ }
193
+
194
+ function extend(destination, source) {
195
+ for (var property in source)
196
+ destination[property] = source[property];
197
+ return destination;
198
+ }
199
+
200
+ function inspect(object) {
201
+ try {
202
+ if (isUndefined(object)) return 'undefined';
203
+ if (object === null) return 'null';
204
+ return object.inspect ? object.inspect() : String(object);
205
+ } catch (e) {
206
+ if (e instanceof RangeError) return '...';
207
+ throw e;
208
+ }
209
+ }
210
+
211
+ function toJSON(value) {
212
+ return Str('', { '': value }, []);
213
+ }
214
+
215
+ function Str(key, holder, stack) {
216
+ var value = holder[key],
217
+ type = typeof value;
218
+
219
+ if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
220
+ value = value.toJSON(key);
221
+ }
222
+
223
+ var _class = _toString.call(value);
224
+
225
+ switch (_class) {
226
+ case NUMBER_CLASS:
227
+ case BOOLEAN_CLASS:
228
+ case STRING_CLASS:
229
+ value = value.valueOf();
230
+ }
231
+
232
+ switch (value) {
233
+ case null: return 'null';
234
+ case true: return 'true';
235
+ case false: return 'false';
236
+ }
237
+
238
+ type = typeof value;
239
+ switch (type) {
240
+ case 'string':
241
+ return value.inspect(true);
242
+ case 'number':
243
+ return isFinite(value) ? String(value) : 'null';
244
+ case 'object':
245
+
246
+ for (var i = 0, length = stack.length; i < length; i++) {
247
+ if (stack[i] === value) { throw new TypeError(); }
248
+ }
249
+ stack.push(value);
250
+
251
+ var partial = [];
252
+ if (_class === ARRAY_CLASS) {
253
+ for (var i = 0, length = value.length; i < length; i++) {
254
+ var str = Str(i, value, stack);
255
+ partial.push(typeof str === 'undefined' ? 'null' : str);
256
+ }
257
+ partial = '[' + partial.join(',') + ']';
258
+ } else {
259
+ var keys = Object.keys(value);
260
+ for (var i = 0, length = keys.length; i < length; i++) {
261
+ var key = keys[i], str = Str(key, value, stack);
262
+ if (typeof str !== "undefined") {
263
+ partial.push(key.inspect(true)+ ':' + str);
264
+ }
265
+ }
266
+ partial = '{' + partial.join(',') + '}';
267
+ }
268
+ stack.pop();
269
+ return partial;
270
+ }
271
+ }
272
+
273
+ function stringify(object) {
274
+ return JSON.stringify(object);
275
+ }
276
+
277
+ function toQueryString(object) {
278
+ return $H(object).toQueryString();
279
+ }
280
+
281
+ function toHTML(object) {
282
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
283
+ }
284
+
285
+ function keys(object) {
286
+ if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
287
+ var results = [];
288
+ for (var property in object) {
289
+ if (object.hasOwnProperty(property)) {
290
+ results.push(property);
291
+ }
292
+ }
293
+ return results;
294
+ }
295
+
296
+ function values(object) {
297
+ var results = [];
298
+ for (var property in object)
299
+ results.push(object[property]);
300
+ return results;
301
+ }
302
+
303
+ function clone(object) {
304
+ return extend({ }, object);
305
+ }
306
+
307
+ function isElement(object) {
308
+ return !!(object && object.nodeType == 1);
309
+ }
310
+
311
+ function isArray(object) {
312
+ return _toString.call(object) === ARRAY_CLASS;
313
+ }
314
+
315
+ var hasNativeIsArray = (typeof Array.isArray == 'function')
316
+ && Array.isArray([]) && !Array.isArray({});
317
+
318
+ if (hasNativeIsArray) {
319
+ isArray = Array.isArray;
320
+ }
321
+
322
+ function isHash(object) {
323
+ return object instanceof Hash;
324
+ }
325
+
326
+ function isFunction(object) {
327
+ return _toString.call(object) === FUNCTION_CLASS;
328
+ }
329
+
330
+ function isString(object) {
331
+ return _toString.call(object) === STRING_CLASS;
332
+ }
333
+
334
+ function isNumber(object) {
335
+ return _toString.call(object) === NUMBER_CLASS;
336
+ }
337
+
338
+ function isDate(object) {
339
+ return _toString.call(object) === DATE_CLASS;
340
+ }
341
+
342
+ function isUndefined(object) {
343
+ return typeof object === "undefined";
344
+ }
345
+
346
+ extend(Object, {
347
+ extend: extend,
348
+ inspect: inspect,
349
+ toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
350
+ toQueryString: toQueryString,
351
+ toHTML: toHTML,
352
+ keys: Object.keys || keys,
353
+ values: values,
354
+ clone: clone,
355
+ isElement: isElement,
356
+ isArray: isArray,
357
+ isHash: isHash,
358
+ isFunction: isFunction,
359
+ isString: isString,
360
+ isNumber: isNumber,
361
+ isDate: isDate,
362
+ isUndefined: isUndefined
363
+ });
364
+ })();
365
+ Object.extend(Function.prototype, (function() {
366
+ var slice = Array.prototype.slice;
367
+
368
+ function update(array, args) {
369
+ var arrayLength = array.length, length = args.length;
370
+ while (length--) array[arrayLength + length] = args[length];
371
+ return array;
372
+ }
373
+
374
+ function merge(array, args) {
375
+ array = slice.call(array, 0);
376
+ return update(array, args);
377
+ }
378
+
379
+ function argumentNames() {
380
+ var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
381
+ .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
382
+ .replace(/\s+/g, '').split(',');
383
+ return names.length == 1 && !names[0] ? [] : names;
384
+ }
385
+
386
+ function bind(context) {
387
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
388
+ var __method = this, args = slice.call(arguments, 1);
389
+ return function() {
390
+ var a = merge(args, arguments);
391
+ return __method.apply(context, a);
392
+ }
393
+ }
394
+
395
+ function bindAsEventListener(context) {
396
+ var __method = this, args = slice.call(arguments, 1);
397
+ return function(event) {
398
+ var a = update([event || window.event], args);
399
+ return __method.apply(context, a);
400
+ }
401
+ }
402
+
403
+ function curry() {
404
+ if (!arguments.length) return this;
405
+ var __method = this, args = slice.call(arguments, 0);
406
+ return function() {
407
+ var a = merge(args, arguments);
408
+ return __method.apply(this, a);
409
+ }
410
+ }
411
+
412
+ function delay(timeout) {
413
+ var __method = this, args = slice.call(arguments, 1);
414
+ timeout = timeout * 1000;
415
+ return window.setTimeout(function() {
416
+ return __method.apply(__method, args);
417
+ }, timeout);
418
+ }
419
+
420
+ function defer() {
421
+ var args = update([0.01], arguments);
422
+ return this.delay.apply(this, args);
423
+ }
424
+
425
+ function wrap(wrapper) {
426
+ var __method = this;
427
+ return function() {
428
+ var a = update([__method.bind(this)], arguments);
429
+ return wrapper.apply(this, a);
430
+ }
431
+ }
432
+
433
+ function methodize() {
434
+ if (this._methodized) return this._methodized;
435
+ var __method = this;
436
+ return this._methodized = function() {
437
+ var a = update([this], arguments);
438
+ return __method.apply(null, a);
439
+ };
440
+ }
441
+
442
+ return {
443
+ argumentNames: argumentNames,
444
+ bind: bind,
445
+ bindAsEventListener: bindAsEventListener,
446
+ curry: curry,
447
+ delay: delay,
448
+ defer: defer,
449
+ wrap: wrap,
450
+ methodize: methodize
451
+ }
452
+ })());
453
+
454
+
455
+
456
+ (function(proto) {
457
+
458
+
459
+ function toISOString() {
460
+ return this.getUTCFullYear() + '-' +
461
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
462
+ this.getUTCDate().toPaddedString(2) + 'T' +
463
+ this.getUTCHours().toPaddedString(2) + ':' +
464
+ this.getUTCMinutes().toPaddedString(2) + ':' +
465
+ this.getUTCSeconds().toPaddedString(2) + 'Z';
466
+ }
467
+
468
+
469
+ function toJSON() {
470
+ return this.toISOString();
471
+ }
472
+
473
+ if (!proto.toISOString) proto.toISOString = toISOString;
474
+ if (!proto.toJSON) proto.toJSON = toJSON;
475
+
476
+ })(Date.prototype);
477
+
478
+
479
+ RegExp.prototype.match = RegExp.prototype.test;
480
+
481
+ RegExp.escape = function(str) {
482
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
483
+ };
484
+ var PeriodicalExecuter = Class.create({
485
+ initialize: function(callback, frequency) {
486
+ this.callback = callback;
487
+ this.frequency = frequency;
488
+ this.currentlyExecuting = false;
489
+
490
+ this.registerCallback();
491
+ },
492
+
493
+ registerCallback: function() {
494
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
495
+ },
496
+
497
+ execute: function() {
498
+ this.callback(this);
499
+ },
500
+
501
+ stop: function() {
502
+ if (!this.timer) return;
503
+ clearInterval(this.timer);
504
+ this.timer = null;
505
+ },
506
+
507
+ onTimerEvent: function() {
508
+ if (!this.currentlyExecuting) {
509
+ try {
510
+ this.currentlyExecuting = true;
511
+ this.execute();
512
+ this.currentlyExecuting = false;
513
+ } catch(e) {
514
+ this.currentlyExecuting = false;
515
+ throw e;
516
+ }
517
+ }
518
+ }
519
+ });
520
+ Object.extend(String, {
521
+ interpret: function(value) {
522
+ return value == null ? '' : String(value);
523
+ },
524
+ specialChar: {
525
+ '\b': '\\b',
526
+ '\t': '\\t',
527
+ '\n': '\\n',
528
+ '\f': '\\f',
529
+ '\r': '\\r',
530
+ '\\': '\\\\'
531
+ }
532
+ });
533
+
534
+ Object.extend(String.prototype, (function() {
535
+ var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
536
+ typeof JSON.parse === 'function' &&
537
+ JSON.parse('{"test": true}').test;
538
+
539
+ function prepareReplacement(replacement) {
540
+ if (Object.isFunction(replacement)) return replacement;
541
+ var template = new Template(replacement);
542
+ return function(match) { return template.evaluate(match) };
543
+ }
544
+
545
+ function gsub(pattern, replacement) {
546
+ var result = '', source = this, match;
547
+ replacement = prepareReplacement(replacement);
548
+
549
+ if (Object.isString(pattern))
550
+ pattern = RegExp.escape(pattern);
551
+
552
+ if (!(pattern.length || pattern.source)) {
553
+ replacement = replacement('');
554
+ return replacement + source.split('').join(replacement) + replacement;
555
+ }
556
+
557
+ while (source.length > 0) {
558
+ if (match = source.match(pattern)) {
559
+ result += source.slice(0, match.index);
560
+ result += String.interpret(replacement(match));
561
+ source = source.slice(match.index + match[0].length);
562
+ } else {
563
+ result += source, source = '';
564
+ }
565
+ }
566
+ return result;
567
+ }
568
+
569
+ function sub(pattern, replacement, count) {
570
+ replacement = prepareReplacement(replacement);
571
+ count = Object.isUndefined(count) ? 1 : count;
572
+
573
+ return this.gsub(pattern, function(match) {
574
+ if (--count < 0) return match[0];
575
+ return replacement(match);
576
+ });
577
+ }
578
+
579
+ function scan(pattern, iterator) {
580
+ this.gsub(pattern, iterator);
581
+ return String(this);
582
+ }
583
+
584
+ function truncate(length, truncation) {
585
+ length = length || 30;
586
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
587
+ return this.length > length ?
588
+ this.slice(0, length - truncation.length) + truncation : String(this);
589
+ }
590
+
591
+ function strip() {
592
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
593
+ }
594
+
595
+ function stripTags() {
596
+ return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
597
+ }
598
+
599
+ function stripScripts() {
600
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
601
+ }
602
+
603
+ function extractScripts() {
604
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
605
+ matchOne = new RegExp(Prototype.ScriptFragment, 'im');
606
+ return (this.match(matchAll) || []).map(function(scriptTag) {
607
+ return (scriptTag.match(matchOne) || ['', ''])[1];
608
+ });
609
+ }
610
+
611
+ function evalScripts() {
612
+ return this.extractScripts().map(function(script) { return eval(script) });
613
+ }
614
+
615
+ function escapeHTML() {
616
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
617
+ }
618
+
619
+ function unescapeHTML() {
620
+ return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
621
+ }
622
+
623
+
624
+ function toQueryParams(separator) {
625
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
626
+ if (!match) return { };
627
+
628
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
629
+ if ((pair = pair.split('='))[0]) {
630
+ var key = decodeURIComponent(pair.shift()),
631
+ value = pair.length > 1 ? pair.join('=') : pair[0];
632
+
633
+ if (value != undefined) value = decodeURIComponent(value);
634
+
635
+ if (key in hash) {
636
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
637
+ hash[key].push(value);
638
+ }
639
+ else hash[key] = value;
640
+ }
641
+ return hash;
642
+ });
643
+ }
644
+
645
+ function toArray() {
646
+ return this.split('');
647
+ }
648
+
649
+ function succ() {
650
+ return this.slice(0, this.length - 1) +
651
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
652
+ }
653
+
654
+ function times(count) {
655
+ return count < 1 ? '' : new Array(count + 1).join(this);
656
+ }
657
+
658
+ function camelize() {
659
+ return this.replace(/-+(.)?/g, function(match, chr) {
660
+ return chr ? chr.toUpperCase() : '';
661
+ });
662
+ }
663
+
664
+ function capitalize() {
665
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
666
+ }
667
+
668
+ function underscore() {
669
+ return this.replace(/::/g, '/')
670
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
671
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
672
+ .replace(/-/g, '_')
673
+ .toLowerCase();
674
+ }
675
+
676
+ function dasherize() {
677
+ return this.replace(/_/g, '-');
678
+ }
679
+
680
+ function inspect(useDoubleQuotes) {
681
+ var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
682
+ if (character in String.specialChar) {
683
+ return String.specialChar[character];
684
+ }
685
+ return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
686
+ });
687
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
688
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
689
+ }
690
+
691
+ function unfilterJSON(filter) {
692
+ return this.replace(filter || Prototype.JSONFilter, '$1');
693
+ }
694
+
695
+ function isJSON() {
696
+ var str = this;
697
+ if (str.blank()) return false;
698
+ str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
699
+ str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
700
+ str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
701
+ return (/^[\],:{}\s]*$/).test(str);
702
+ }
703
+
704
+ function evalJSON(sanitize) {
705
+ var json = this.unfilterJSON(),
706
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
707
+ if (cx.test(json)) {
708
+ json = json.replace(cx, function (a) {
709
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
710
+ });
711
+ }
712
+ try {
713
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
714
+ } catch (e) { }
715
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
716
+ }
717
+
718
+ function parseJSON() {
719
+ var json = this.unfilterJSON();
720
+ return JSON.parse(json);
721
+ }
722
+
723
+ function include(pattern) {
724
+ return this.indexOf(pattern) > -1;
725
+ }
726
+
727
+ function startsWith(pattern) {
728
+ return this.lastIndexOf(pattern, 0) === 0;
729
+ }
730
+
731
+ function endsWith(pattern) {
732
+ var d = this.length - pattern.length;
733
+ return d >= 0 && this.indexOf(pattern, d) === d;
734
+ }
735
+
736
+ function empty() {
737
+ return this == '';
738
+ }
739
+
740
+ function blank() {
741
+ return /^\s*$/.test(this);
742
+ }
743
+
744
+ function interpolate(object, pattern) {
745
+ return new Template(this, pattern).evaluate(object);
746
+ }
747
+
748
+ return {
749
+ gsub: gsub,
750
+ sub: sub,
751
+ scan: scan,
752
+ truncate: truncate,
753
+ strip: String.prototype.trim || strip,
754
+ stripTags: stripTags,
755
+ stripScripts: stripScripts,
756
+ extractScripts: extractScripts,
757
+ evalScripts: evalScripts,
758
+ escapeHTML: escapeHTML,
759
+ unescapeHTML: unescapeHTML,
760
+ toQueryParams: toQueryParams,
761
+ parseQuery: toQueryParams,
762
+ toArray: toArray,
763
+ succ: succ,
764
+ times: times,
765
+ camelize: camelize,
766
+ capitalize: capitalize,
767
+ underscore: underscore,
768
+ dasherize: dasherize,
769
+ inspect: inspect,
770
+ unfilterJSON: unfilterJSON,
771
+ isJSON: isJSON,
772
+ evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
773
+ include: include,
774
+ startsWith: startsWith,
775
+ endsWith: endsWith,
776
+ empty: empty,
777
+ blank: blank,
778
+ interpolate: interpolate
779
+ };
780
+ })());
781
+
782
+ var Template = Class.create({
783
+ initialize: function(template, pattern) {
784
+ this.template = template.toString();
785
+ this.pattern = pattern || Template.Pattern;
786
+ },
787
+
788
+ evaluate: function(object) {
789
+ if (object && Object.isFunction(object.toTemplateReplacements))
790
+ object = object.toTemplateReplacements();
791
+
792
+ return this.template.gsub(this.pattern, function(match) {
793
+ if (object == null) return (match[1] + '');
794
+
795
+ var before = match[1] || '';
796
+ if (before == '\\') return match[2];
797
+
798
+ var ctx = object, expr = match[3],
799
+ pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
800
+
801
+ match = pattern.exec(expr);
802
+ if (match == null) return before;
803
+
804
+ while (match != null) {
805
+ var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
806
+ ctx = ctx[comp];
807
+ if (null == ctx || '' == match[3]) break;
808
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
809
+ match = pattern.exec(expr);
810
+ }
811
+
812
+ return before + String.interpret(ctx);
813
+ });
814
+ }
815
+ });
816
+ Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
817
+
818
+ var $break = { };
819
+
820
+ var Enumerable = (function() {
821
+ function each(iterator, context) {
822
+ var index = 0;
823
+ try {
824
+ this._each(function(value) {
825
+ iterator.call(context, value, index++);
826
+ });
827
+ } catch (e) {
828
+ if (e != $break) throw e;
829
+ }
830
+ return this;
831
+ }
832
+
833
+ function eachSlice(number, iterator, context) {
834
+ var index = -number, slices = [], array = this.toArray();
835
+ if (number < 1) return array;
836
+ while ((index += number) < array.length)
837
+ slices.push(array.slice(index, index+number));
838
+ return slices.collect(iterator, context);
839
+ }
840
+
841
+ function all(iterator, context) {
842
+ iterator = iterator || Prototype.K;
843
+ var result = true;
844
+ this.each(function(value, index) {
845
+ result = result && !!iterator.call(context, value, index);
846
+ if (!result) throw $break;
847
+ });
848
+ return result;
849
+ }
850
+
851
+ function any(iterator, context) {
852
+ iterator = iterator || Prototype.K;
853
+ var result = false;
854
+ this.each(function(value, index) {
855
+ if (result = !!iterator.call(context, value, index))
856
+ throw $break;
857
+ });
858
+ return result;
859
+ }
860
+
861
+ function collect(iterator, context) {
862
+ iterator = iterator || Prototype.K;
863
+ var results = [];
864
+ this.each(function(value, index) {
865
+ results.push(iterator.call(context, value, index));
866
+ });
867
+ return results;
868
+ }
869
+
870
+ function detect(iterator, context) {
871
+ var result;
872
+ this.each(function(value, index) {
873
+ if (iterator.call(context, value, index)) {
874
+ result = value;
875
+ throw $break;
876
+ }
877
+ });
878
+ return result;
879
+ }
880
+
881
+ function findAll(iterator, context) {
882
+ var results = [];
883
+ this.each(function(value, index) {
884
+ if (iterator.call(context, value, index))
885
+ results.push(value);
886
+ });
887
+ return results;
888
+ }
889
+
890
+ function grep(filter, iterator, context) {
891
+ iterator = iterator || Prototype.K;
892
+ var results = [];
893
+
894
+ if (Object.isString(filter))
895
+ filter = new RegExp(RegExp.escape(filter));
896
+
897
+ this.each(function(value, index) {
898
+ if (filter.match(value))
899
+ results.push(iterator.call(context, value, index));
900
+ });
901
+ return results;
902
+ }
903
+
904
+ function include(object) {
905
+ if (Object.isFunction(this.indexOf))
906
+ if (this.indexOf(object) != -1) return true;
907
+
908
+ var found = false;
909
+ this.each(function(value) {
910
+ if (value == object) {
911
+ found = true;
912
+ throw $break;
913
+ }
914
+ });
915
+ return found;
916
+ }
917
+
918
+ function inGroupsOf(number, fillWith) {
919
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
920
+ return this.eachSlice(number, function(slice) {
921
+ while(slice.length < number) slice.push(fillWith);
922
+ return slice;
923
+ });
924
+ }
925
+
926
+ function inject(memo, iterator, context) {
927
+ this.each(function(value, index) {
928
+ memo = iterator.call(context, memo, value, index);
929
+ });
930
+ return memo;
931
+ }
932
+
933
+ function invoke(method) {
934
+ var args = $A(arguments).slice(1);
935
+ return this.map(function(value) {
936
+ return value[method].apply(value, args);
937
+ });
938
+ }
939
+
940
+ function max(iterator, context) {
941
+ iterator = iterator || Prototype.K;
942
+ var result;
943
+ this.each(function(value, index) {
944
+ value = iterator.call(context, value, index);
945
+ if (result == null || value >= result)
946
+ result = value;
947
+ });
948
+ return result;
949
+ }
950
+
951
+ function min(iterator, context) {
952
+ iterator = iterator || Prototype.K;
953
+ var result;
954
+ this.each(function(value, index) {
955
+ value = iterator.call(context, value, index);
956
+ if (result == null || value < result)
957
+ result = value;
958
+ });
959
+ return result;
960
+ }
961
+
962
+ function partition(iterator, context) {
963
+ iterator = iterator || Prototype.K;
964
+ var trues = [], falses = [];
965
+ this.each(function(value, index) {
966
+ (iterator.call(context, value, index) ?
967
+ trues : falses).push(value);
968
+ });
969
+ return [trues, falses];
970
+ }
971
+
972
+ function pluck(property) {
973
+ var results = [];
974
+ this.each(function(value) {
975
+ results.push(value[property]);
976
+ });
977
+ return results;
978
+ }
979
+
980
+ function reject(iterator, context) {
981
+ var results = [];
982
+ this.each(function(value, index) {
983
+ if (!iterator.call(context, value, index))
984
+ results.push(value);
985
+ });
986
+ return results;
987
+ }
988
+
989
+ function sortBy(iterator, context) {
990
+ return this.map(function(value, index) {
991
+ return {
992
+ value: value,
993
+ criteria: iterator.call(context, value, index)
994
+ };
995
+ }).sort(function(left, right) {
996
+ var a = left.criteria, b = right.criteria;
997
+ return a < b ? -1 : a > b ? 1 : 0;
998
+ }).pluck('value');
999
+ }
1000
+
1001
+ function toArray() {
1002
+ return this.map();
1003
+ }
1004
+
1005
+ function zip() {
1006
+ var iterator = Prototype.K, args = $A(arguments);
1007
+ if (Object.isFunction(args.last()))
1008
+ iterator = args.pop();
1009
+
1010
+ var collections = [this].concat(args).map($A);
1011
+ return this.map(function(value, index) {
1012
+ return iterator(collections.pluck(index));
1013
+ });
1014
+ }
1015
+
1016
+ function size() {
1017
+ return this.toArray().length;
1018
+ }
1019
+
1020
+ function inspect() {
1021
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
1022
+ }
1023
+
1024
+
1025
+
1026
+
1027
+
1028
+
1029
+
1030
+
1031
+
1032
+ return {
1033
+ each: each,
1034
+ eachSlice: eachSlice,
1035
+ all: all,
1036
+ every: all,
1037
+ any: any,
1038
+ some: any,
1039
+ collect: collect,
1040
+ map: collect,
1041
+ detect: detect,
1042
+ findAll: findAll,
1043
+ select: findAll,
1044
+ filter: findAll,
1045
+ grep: grep,
1046
+ include: include,
1047
+ member: include,
1048
+ inGroupsOf: inGroupsOf,
1049
+ inject: inject,
1050
+ invoke: invoke,
1051
+ max: max,
1052
+ min: min,
1053
+ partition: partition,
1054
+ pluck: pluck,
1055
+ reject: reject,
1056
+ sortBy: sortBy,
1057
+ toArray: toArray,
1058
+ entries: toArray,
1059
+ zip: zip,
1060
+ size: size,
1061
+ inspect: inspect,
1062
+ find: detect
1063
+ };
1064
+ })();
1065
+
1066
+ function $A(iterable) {
1067
+ if (!iterable) return [];
1068
+ if ('toArray' in Object(iterable)) return iterable.toArray();
1069
+ var length = iterable.length || 0, results = new Array(length);
1070
+ while (length--) results[length] = iterable[length];
1071
+ return results;
1072
+ }
1073
+
1074
+
1075
+ function $w(string) {
1076
+ if (!Object.isString(string)) return [];
1077
+ string = string.strip();
1078
+ return string ? string.split(/\s+/) : [];
1079
+ }
1080
+
1081
+ Array.from = $A;
1082
+
1083
+
1084
+ (function() {
1085
+ var arrayProto = Array.prototype,
1086
+ slice = arrayProto.slice,
1087
+ _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
1088
+
1089
+ function each(iterator, context) {
1090
+ for (var i = 0, length = this.length >>> 0; i < length; i++) {
1091
+ if (i in this) iterator.call(context, this[i], i, this);
1092
+ }
1093
+ }
1094
+ if (!_each) _each = each;
1095
+
1096
+ function clear() {
1097
+ this.length = 0;
1098
+ return this;
1099
+ }
1100
+
1101
+ function first() {
1102
+ return this[0];
1103
+ }
1104
+
1105
+ function last() {
1106
+ return this[this.length - 1];
1107
+ }
1108
+
1109
+ function compact() {
1110
+ return this.select(function(value) {
1111
+ return value != null;
1112
+ });
1113
+ }
1114
+
1115
+ function flatten() {
1116
+ return this.inject([], function(array, value) {
1117
+ if (Object.isArray(value))
1118
+ return array.concat(value.flatten());
1119
+ array.push(value);
1120
+ return array;
1121
+ });
1122
+ }
1123
+
1124
+ function without() {
1125
+ var values = slice.call(arguments, 0);
1126
+ return this.select(function(value) {
1127
+ return !values.include(value);
1128
+ });
1129
+ }
1130
+
1131
+ function reverse(inline) {
1132
+ return (inline === false ? this.toArray() : this)._reverse();
1133
+ }
1134
+
1135
+ function uniq(sorted) {
1136
+ return this.inject([], function(array, value, index) {
1137
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1138
+ array.push(value);
1139
+ return array;
1140
+ });
1141
+ }
1142
+
1143
+ function intersect(array) {
1144
+ return this.uniq().findAll(function(item) {
1145
+ return array.detect(function(value) { return item === value });
1146
+ });
1147
+ }
1148
+
1149
+
1150
+ function clone() {
1151
+ return slice.call(this, 0);
1152
+ }
1153
+
1154
+ function size() {
1155
+ return this.length;
1156
+ }
1157
+
1158
+ function inspect() {
1159
+ return '[' + this.map(Object.inspect).join(', ') + ']';
1160
+ }
1161
+
1162
+ function indexOf(item, i) {
1163
+ i || (i = 0);
1164
+ var length = this.length;
1165
+ if (i < 0) i = length + i;
1166
+ for (; i < length; i++)
1167
+ if (this[i] === item) return i;
1168
+ return -1;
1169
+ }
1170
+
1171
+ function lastIndexOf(item, i) {
1172
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
1173
+ var n = this.slice(0, i).reverse().indexOf(item);
1174
+ return (n < 0) ? n : i - n - 1;
1175
+ }
1176
+
1177
+ function concat() {
1178
+ var array = slice.call(this, 0), item;
1179
+ for (var i = 0, length = arguments.length; i < length; i++) {
1180
+ item = arguments[i];
1181
+ if (Object.isArray(item) && !('callee' in item)) {
1182
+ for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
1183
+ array.push(item[j]);
1184
+ } else {
1185
+ array.push(item);
1186
+ }
1187
+ }
1188
+ return array;
1189
+ }
1190
+
1191
+ Object.extend(arrayProto, Enumerable);
1192
+
1193
+ if (!arrayProto._reverse)
1194
+ arrayProto._reverse = arrayProto.reverse;
1195
+
1196
+ Object.extend(arrayProto, {
1197
+ _each: _each,
1198
+ clear: clear,
1199
+ first: first,
1200
+ last: last,
1201
+ compact: compact,
1202
+ flatten: flatten,
1203
+ without: without,
1204
+ reverse: reverse,
1205
+ uniq: uniq,
1206
+ intersect: intersect,
1207
+ clone: clone,
1208
+ toArray: clone,
1209
+ size: size,
1210
+ inspect: inspect
1211
+ });
1212
+
1213
+ var CONCAT_ARGUMENTS_BUGGY = (function() {
1214
+ return [].concat(arguments)[0][0] !== 1;
1215
+ })(1,2)
1216
+
1217
+ if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1218
+
1219
+ if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1220
+ if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1221
+ })();
1222
+ function $H(object) {
1223
+ return new Hash(object);
1224
+ };
1225
+
1226
+ var Hash = Class.create(Enumerable, (function() {
1227
+ function initialize(object) {
1228
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1229
+ }
1230
+
1231
+
1232
+ function _each(iterator) {
1233
+ for (var key in this._object) {
1234
+ var value = this._object[key], pair = [key, value];
1235
+ pair.key = key;
1236
+ pair.value = value;
1237
+ iterator(pair);
1238
+ }
1239
+ }
1240
+
1241
+ function set(key, value) {
1242
+ return this._object[key] = value;
1243
+ }
1244
+
1245
+ function get(key) {
1246
+ if (this._object[key] !== Object.prototype[key])
1247
+ return this._object[key];
1248
+ }
1249
+
1250
+ function unset(key) {
1251
+ var value = this._object[key];
1252
+ delete this._object[key];
1253
+ return value;
1254
+ }
1255
+
1256
+ function toObject() {
1257
+ return Object.clone(this._object);
1258
+ }
1259
+
1260
+
1261
+
1262
+ function keys() {
1263
+ return this.pluck('key');
1264
+ }
1265
+
1266
+ function values() {
1267
+ return this.pluck('value');
1268
+ }
1269
+
1270
+ function index(value) {
1271
+ var match = this.detect(function(pair) {
1272
+ return pair.value === value;
1273
+ });
1274
+ return match && match.key;
1275
+ }
1276
+
1277
+ function merge(object) {
1278
+ return this.clone().update(object);
1279
+ }
1280
+
1281
+ function update(object) {
1282
+ return new Hash(object).inject(this, function(result, pair) {
1283
+ result.set(pair.key, pair.value);
1284
+ return result;
1285
+ });
1286
+ }
1287
+
1288
+ function toQueryPair(key, value) {
1289
+ if (Object.isUndefined(value)) return key;
1290
+ return key + '=' + encodeURIComponent(String.interpret(value));
1291
+ }
1292
+
1293
+ function toQueryString() {
1294
+ return this.inject([], function(results, pair) {
1295
+ var key = encodeURIComponent(pair.key), values = pair.value;
1296
+
1297
+ if (values && typeof values == 'object') {
1298
+ if (Object.isArray(values)) {
1299
+ var queryValues = [];
1300
+ for (var i = 0, len = values.length, value; i < len; i++) {
1301
+ value = values[i];
1302
+ queryValues.push(toQueryPair(key, value));
1303
+ }
1304
+ return results.concat(queryValues);
1305
+ }
1306
+ } else results.push(toQueryPair(key, values));
1307
+ return results;
1308
+ }).join('&');
1309
+ }
1310
+
1311
+ function inspect() {
1312
+ return '#<Hash:{' + this.map(function(pair) {
1313
+ return pair.map(Object.inspect).join(': ');
1314
+ }).join(', ') + '}>';
1315
+ }
1316
+
1317
+ function clone() {
1318
+ return new Hash(this);
1319
+ }
1320
+
1321
+ return {
1322
+ initialize: initialize,
1323
+ _each: _each,
1324
+ set: set,
1325
+ get: get,
1326
+ unset: unset,
1327
+ toObject: toObject,
1328
+ toTemplateReplacements: toObject,
1329
+ keys: keys,
1330
+ values: values,
1331
+ index: index,
1332
+ merge: merge,
1333
+ update: update,
1334
+ toQueryString: toQueryString,
1335
+ inspect: inspect,
1336
+ toJSON: toObject,
1337
+ clone: clone
1338
+ };
1339
+ })());
1340
+
1341
+ Hash.from = $H;
1342
+ Object.extend(Number.prototype, (function() {
1343
+ function toColorPart() {
1344
+ return this.toPaddedString(2, 16);
1345
+ }
1346
+
1347
+ function succ() {
1348
+ return this + 1;
1349
+ }
1350
+
1351
+ function times(iterator, context) {
1352
+ $R(0, this, true).each(iterator, context);
1353
+ return this;
1354
+ }
1355
+
1356
+ function toPaddedString(length, radix) {
1357
+ var string = this.toString(radix || 10);
1358
+ return '0'.times(length - string.length) + string;
1359
+ }
1360
+
1361
+ function abs() {
1362
+ return Math.abs(this);
1363
+ }
1364
+
1365
+ function round() {
1366
+ return Math.round(this);
1367
+ }
1368
+
1369
+ function ceil() {
1370
+ return Math.ceil(this);
1371
+ }
1372
+
1373
+ function floor() {
1374
+ return Math.floor(this);
1375
+ }
1376
+
1377
+ return {
1378
+ toColorPart: toColorPart,
1379
+ succ: succ,
1380
+ times: times,
1381
+ toPaddedString: toPaddedString,
1382
+ abs: abs,
1383
+ round: round,
1384
+ ceil: ceil,
1385
+ floor: floor
1386
+ };
1387
+ })());
1388
+
1389
+ function $R(start, end, exclusive) {
1390
+ return new ObjectRange(start, end, exclusive);
1391
+ }
1392
+
1393
+ var ObjectRange = Class.create(Enumerable, (function() {
1394
+ function initialize(start, end, exclusive) {
1395
+ this.start = start;
1396
+ this.end = end;
1397
+ this.exclusive = exclusive;
1398
+ }
1399
+
1400
+ function _each(iterator) {
1401
+ var value = this.start;
1402
+ while (this.include(value)) {
1403
+ iterator(value);
1404
+ value = value.succ();
1405
+ }
1406
+ }
1407
+
1408
+ function include(value) {
1409
+ if (value < this.start)
1410
+ return false;
1411
+ if (this.exclusive)
1412
+ return value < this.end;
1413
+ return value <= this.end;
1414
+ }
1415
+
1416
+ return {
1417
+ initialize: initialize,
1418
+ _each: _each,
1419
+ include: include
1420
+ };
1421
+ })());
1422
+
1423
+
1424
+
1425
+ var Ajax = {
1426
+ getTransport: function() {
1427
+ return Try.these(
1428
+ function() {return new XMLHttpRequest()},
1429
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1430
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1431
+ ) || false;
1432
+ },
1433
+
1434
+ activeRequestCount: 0
1435
+ };
1436
+
1437
+ Ajax.Responders = {
1438
+ responders: [],
1439
+
1440
+ _each: function(iterator) {
1441
+ this.responders._each(iterator);
1442
+ },
1443
+
1444
+ register: function(responder) {
1445
+ if (!this.include(responder))
1446
+ this.responders.push(responder);
1447
+ },
1448
+
1449
+ unregister: function(responder) {
1450
+ this.responders = this.responders.without(responder);
1451
+ },
1452
+
1453
+ dispatch: function(callback, request, transport, json) {
1454
+ this.each(function(responder) {
1455
+ if (Object.isFunction(responder[callback])) {
1456
+ try {
1457
+ responder[callback].apply(responder, [request, transport, json]);
1458
+ } catch (e) { }
1459
+ }
1460
+ });
1461
+ }
1462
+ };
1463
+
1464
+ Object.extend(Ajax.Responders, Enumerable);
1465
+
1466
+ Ajax.Responders.register({
1467
+ onCreate: function() { Ajax.activeRequestCount++ },
1468
+ onComplete: function() { Ajax.activeRequestCount-- }
1469
+ });
1470
+ Ajax.Base = Class.create({
1471
+ initialize: function(options) {
1472
+ this.options = {
1473
+ method: 'post',
1474
+ asynchronous: true,
1475
+ contentType: 'application/x-www-form-urlencoded',
1476
+ encoding: 'UTF-8',
1477
+ parameters: '',
1478
+ evalJSON: true,
1479
+ evalJS: true
1480
+ };
1481
+ Object.extend(this.options, options || { });
1482
+
1483
+ this.options.method = this.options.method.toLowerCase();
1484
+
1485
+ if (Object.isHash(this.options.parameters))
1486
+ this.options.parameters = this.options.parameters.toObject();
1487
+ }
1488
+ });
1489
+ Ajax.Request = Class.create(Ajax.Base, {
1490
+ _complete: false,
1491
+
1492
+ initialize: function($super, url, options) {
1493
+ $super(options);
1494
+ this.transport = Ajax.getTransport();
1495
+ this.request(url);
1496
+ },
1497
+
1498
+ request: function(url) {
1499
+ this.url = url;
1500
+ this.method = this.options.method;
1501
+ var params = Object.isString(this.options.parameters) ?
1502
+ this.options.parameters :
1503
+ Object.toQueryString(this.options.parameters);
1504
+
1505
+ if (!['get', 'post'].include(this.method)) {
1506
+ params += (params ? '&' : '') + "_method=" + this.method;
1507
+ this.method = 'post';
1508
+ }
1509
+
1510
+ if (params && this.method === 'get') {
1511
+ this.url += (this.url.include('?') ? '&' : '?') + params;
1512
+ }
1513
+
1514
+ this.parameters = params.toQueryParams();
1515
+
1516
+ try {
1517
+ var response = new Ajax.Response(this);
1518
+ if (this.options.onCreate) this.options.onCreate(response);
1519
+ Ajax.Responders.dispatch('onCreate', this, response);
1520
+
1521
+ this.transport.open(this.method.toUpperCase(), this.url,
1522
+ this.options.asynchronous);
1523
+
1524
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1525
+
1526
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
1527
+ this.setRequestHeaders();
1528
+
1529
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1530
+ this.transport.send(this.body);
1531
+
1532
+ /* Force Firefox to handle ready state 4 for synchronous requests */
1533
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
1534
+ this.onStateChange();
1535
+
1536
+ }
1537
+ catch (e) {
1538
+ this.dispatchException(e);
1539
+ }
1540
+ },
1541
+
1542
+ onStateChange: function() {
1543
+ var readyState = this.transport.readyState;
1544
+ if (readyState > 1 && !((readyState == 4) && this._complete))
1545
+ this.respondToReadyState(this.transport.readyState);
1546
+ },
1547
+
1548
+ setRequestHeaders: function() {
1549
+ var headers = {
1550
+ 'X-Requested-With': 'XMLHttpRequest',
1551
+ 'X-Prototype-Version': Prototype.Version,
1552
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1553
+ };
1554
+
1555
+ if (this.method == 'post') {
1556
+ headers['Content-type'] = this.options.contentType +
1557
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
1558
+
1559
+ /* Force "Connection: close" for older Mozilla browsers to work
1560
+ * around a bug where XMLHttpRequest sends an incorrect
1561
+ * Content-length header. See Mozilla Bugzilla #246651.
1562
+ */
1563
+ if (this.transport.overrideMimeType &&
1564
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1565
+ headers['Connection'] = 'close';
1566
+ }
1567
+
1568
+ if (typeof this.options.requestHeaders == 'object') {
1569
+ var extras = this.options.requestHeaders;
1570
+
1571
+ if (Object.isFunction(extras.push))
1572
+ for (var i = 0, length = extras.length; i < length; i += 2)
1573
+ headers[extras[i]] = extras[i+1];
1574
+ else
1575
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1576
+ }
1577
+
1578
+ for (var name in headers)
1579
+ this.transport.setRequestHeader(name, headers[name]);
1580
+ },
1581
+
1582
+ success: function() {
1583
+ var status = this.getStatus();
1584
+ return !status || (status >= 200 && status < 300) || status == 304;
1585
+ },
1586
+
1587
+ getStatus: function() {
1588
+ try {
1589
+ if (this.transport.status === 1223) return 204;
1590
+ return this.transport.status || 0;
1591
+ } catch (e) { return 0 }
1592
+ },
1593
+
1594
+ respondToReadyState: function(readyState) {
1595
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1596
+
1597
+ if (state == 'Complete') {
1598
+ try {
1599
+ this._complete = true;
1600
+ (this.options['on' + response.status]
1601
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1602
+ || Prototype.emptyFunction)(response, response.headerJSON);
1603
+ } catch (e) {
1604
+ this.dispatchException(e);
1605
+ }
1606
+
1607
+ var contentType = response.getHeader('Content-type');
1608
+ if (this.options.evalJS == 'force'
1609
+ || (this.options.evalJS && this.isSameOrigin() && contentType
1610
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1611
+ this.evalResponse();
1612
+ }
1613
+
1614
+ try {
1615
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1616
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1617
+ } catch (e) {
1618
+ this.dispatchException(e);
1619
+ }
1620
+
1621
+ if (state == 'Complete') {
1622
+ this.transport.onreadystatechange = Prototype.emptyFunction;
1623
+ }
1624
+ },
1625
+
1626
+ isSameOrigin: function() {
1627
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1628
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1629
+ protocol: location.protocol,
1630
+ domain: document.domain,
1631
+ port: location.port ? ':' + location.port : ''
1632
+ }));
1633
+ },
1634
+
1635
+ getHeader: function(name) {
1636
+ try {
1637
+ return this.transport.getResponseHeader(name) || null;
1638
+ } catch (e) { return null; }
1639
+ },
1640
+
1641
+ evalResponse: function() {
1642
+ try {
1643
+ return eval((this.transport.responseText || '').unfilterJSON());
1644
+ } catch (e) {
1645
+ this.dispatchException(e);
1646
+ }
1647
+ },
1648
+
1649
+ dispatchException: function(exception) {
1650
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
1651
+ Ajax.Responders.dispatch('onException', this, exception);
1652
+ }
1653
+ });
1654
+
1655
+ Ajax.Request.Events =
1656
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1657
+
1658
+
1659
+
1660
+
1661
+
1662
+
1663
+
1664
+
1665
+ Ajax.Response = Class.create({
1666
+ initialize: function(request){
1667
+ this.request = request;
1668
+ var transport = this.transport = request.transport,
1669
+ readyState = this.readyState = transport.readyState;
1670
+
1671
+ if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1672
+ this.status = this.getStatus();
1673
+ this.statusText = this.getStatusText();
1674
+ this.responseText = String.interpret(transport.responseText);
1675
+ this.headerJSON = this._getHeaderJSON();
1676
+ }
1677
+
1678
+ if (readyState == 4) {
1679
+ var xml = transport.responseXML;
1680
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
1681
+ this.responseJSON = this._getResponseJSON();
1682
+ }
1683
+ },
1684
+
1685
+ status: 0,
1686
+
1687
+ statusText: '',
1688
+
1689
+ getStatus: Ajax.Request.prototype.getStatus,
1690
+
1691
+ getStatusText: function() {
1692
+ try {
1693
+ return this.transport.statusText || '';
1694
+ } catch (e) { return '' }
1695
+ },
1696
+
1697
+ getHeader: Ajax.Request.prototype.getHeader,
1698
+
1699
+ getAllHeaders: function() {
1700
+ try {
1701
+ return this.getAllResponseHeaders();
1702
+ } catch (e) { return null }
1703
+ },
1704
+
1705
+ getResponseHeader: function(name) {
1706
+ return this.transport.getResponseHeader(name);
1707
+ },
1708
+
1709
+ getAllResponseHeaders: function() {
1710
+ return this.transport.getAllResponseHeaders();
1711
+ },
1712
+
1713
+ _getHeaderJSON: function() {
1714
+ var json = this.getHeader('X-JSON');
1715
+ if (!json) return null;
1716
+ json = decodeURIComponent(escape(json));
1717
+ try {
1718
+ return json.evalJSON(this.request.options.sanitizeJSON ||
1719
+ !this.request.isSameOrigin());
1720
+ } catch (e) {
1721
+ this.request.dispatchException(e);
1722
+ }
1723
+ },
1724
+
1725
+ _getResponseJSON: function() {
1726
+ var options = this.request.options;
1727
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
1728
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
1729
+ this.responseText.blank())
1730
+ return null;
1731
+ try {
1732
+ return this.responseText.evalJSON(options.sanitizeJSON ||
1733
+ !this.request.isSameOrigin());
1734
+ } catch (e) {
1735
+ this.request.dispatchException(e);
1736
+ }
1737
+ }
1738
+ });
1739
+
1740
+ Ajax.Updater = Class.create(Ajax.Request, {
1741
+ initialize: function($super, container, url, options) {
1742
+ this.container = {
1743
+ success: (container.success || container),
1744
+ failure: (container.failure || (container.success ? null : container))
1745
+ };
1746
+
1747
+ options = Object.clone(options);
1748
+ var onComplete = options.onComplete;
1749
+ options.onComplete = (function(response, json) {
1750
+ this.updateContent(response.responseText);
1751
+ if (Object.isFunction(onComplete)) onComplete(response, json);
1752
+ }).bind(this);
1753
+
1754
+ $super(url, options);
1755
+ },
1756
+
1757
+ updateContent: function(responseText) {
1758
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
1759
+ options = this.options;
1760
+
1761
+ if (!options.evalScripts) responseText = responseText.stripScripts();
1762
+
1763
+ if (receiver = $(receiver)) {
1764
+ if (options.insertion) {
1765
+ if (Object.isString(options.insertion)) {
1766
+ var insertion = { }; insertion[options.insertion] = responseText;
1767
+ receiver.insert(insertion);
1768
+ }
1769
+ else options.insertion(receiver, responseText);
1770
+ }
1771
+ else receiver.update(responseText);
1772
+ }
1773
+ }
1774
+ });
1775
+
1776
+ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1777
+ initialize: function($super, container, url, options) {
1778
+ $super(options);
1779
+ this.onComplete = this.options.onComplete;
1780
+
1781
+ this.frequency = (this.options.frequency || 2);
1782
+ this.decay = (this.options.decay || 1);
1783
+
1784
+ this.updater = { };
1785
+ this.container = container;
1786
+ this.url = url;
1787
+
1788
+ this.start();
1789
+ },
1790
+
1791
+ start: function() {
1792
+ this.options.onComplete = this.updateComplete.bind(this);
1793
+ this.onTimerEvent();
1794
+ },
1795
+
1796
+ stop: function() {
1797
+ this.updater.options.onComplete = undefined;
1798
+ clearTimeout(this.timer);
1799
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1800
+ },
1801
+
1802
+ updateComplete: function(response) {
1803
+ if (this.options.decay) {
1804
+ this.decay = (response.responseText == this.lastText ?
1805
+ this.decay * this.options.decay : 1);
1806
+
1807
+ this.lastText = response.responseText;
1808
+ }
1809
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1810
+ },
1811
+
1812
+ onTimerEvent: function() {
1813
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
1814
+ }
1815
+ });
1816
+
1817
+
1818
+ function $(element) {
1819
+ if (arguments.length > 1) {
1820
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1821
+ elements.push($(arguments[i]));
1822
+ return elements;
1823
+ }
1824
+ if (Object.isString(element))
1825
+ element = document.getElementById(element);
1826
+ return Element.extend(element);
1827
+ }
1828
+
1829
+ if (Prototype.BrowserFeatures.XPath) {
1830
+ document._getElementsByXPath = function(expression, parentElement) {
1831
+ var results = [];
1832
+ var query = document.evaluate(expression, $(parentElement) || document,
1833
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1834
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
1835
+ results.push(Element.extend(query.snapshotItem(i)));
1836
+ return results;
1837
+ };
1838
+ }
1839
+
1840
+ /*--------------------------------------------------------------------------*/
1841
+
1842
+ if (!Node) var Node = { };
1843
+
1844
+ if (!Node.ELEMENT_NODE) {
1845
+ Object.extend(Node, {
1846
+ ELEMENT_NODE: 1,
1847
+ ATTRIBUTE_NODE: 2,
1848
+ TEXT_NODE: 3,
1849
+ CDATA_SECTION_NODE: 4,
1850
+ ENTITY_REFERENCE_NODE: 5,
1851
+ ENTITY_NODE: 6,
1852
+ PROCESSING_INSTRUCTION_NODE: 7,
1853
+ COMMENT_NODE: 8,
1854
+ DOCUMENT_NODE: 9,
1855
+ DOCUMENT_TYPE_NODE: 10,
1856
+ DOCUMENT_FRAGMENT_NODE: 11,
1857
+ NOTATION_NODE: 12
1858
+ });
1859
+ }
1860
+
1861
+
1862
+
1863
+ (function(global) {
1864
+ function shouldUseCache(tagName, attributes) {
1865
+ if (tagName === 'select') return false;
1866
+ if ('type' in attributes) return false;
1867
+ return true;
1868
+ }
1869
+
1870
+ var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
1871
+ try {
1872
+ var el = document.createElement('<input name="x">');
1873
+ return el.tagName.toLowerCase() === 'input' && el.name === 'x';
1874
+ }
1875
+ catch(err) {
1876
+ return false;
1877
+ }
1878
+ })();
1879
+
1880
+ var element = global.Element;
1881
+
1882
+ global.Element = function(tagName, attributes) {
1883
+ attributes = attributes || { };
1884
+ tagName = tagName.toLowerCase();
1885
+ var cache = Element.cache;
1886
+
1887
+ if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
1888
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
1889
+ delete attributes.name;
1890
+ return Element.writeAttribute(document.createElement(tagName), attributes);
1891
+ }
1892
+
1893
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1894
+
1895
+ var node = shouldUseCache(tagName, attributes) ?
1896
+ cache[tagName].cloneNode(false) : document.createElement(tagName);
1897
+
1898
+ return Element.writeAttribute(node, attributes);
1899
+ };
1900
+
1901
+ Object.extend(global.Element, element || { });
1902
+ if (element) global.Element.prototype = element.prototype;
1903
+
1904
+ })(this);
1905
+
1906
+ Element.idCounter = 1;
1907
+ Element.cache = { };
1908
+
1909
+ Element._purgeElement = function(element) {
1910
+ var uid = element._prototypeUID;
1911
+ if (uid) {
1912
+ Element.stopObserving(element);
1913
+ element._prototypeUID = void 0;
1914
+ delete Element.Storage[uid];
1915
+ }
1916
+ }
1917
+
1918
+ Element.Methods = {
1919
+ visible: function(element) {
1920
+ return $(element).style.display != 'none';
1921
+ },
1922
+
1923
+ toggle: function(element) {
1924
+ element = $(element);
1925
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
1926
+ return element;
1927
+ },
1928
+
1929
+ hide: function(element) {
1930
+ element = $(element);
1931
+ element.style.display = 'none';
1932
+ return element;
1933
+ },
1934
+
1935
+ show: function(element) {
1936
+ element = $(element);
1937
+ element.style.display = '';
1938
+ return element;
1939
+ },
1940
+
1941
+ remove: function(element) {
1942
+ element = $(element);
1943
+ element.parentNode.removeChild(element);
1944
+ return element;
1945
+ },
1946
+
1947
+ update: (function(){
1948
+
1949
+ var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
1950
+ var el = document.createElement("select"),
1951
+ isBuggy = true;
1952
+ el.innerHTML = "<option value=\"test\">test</option>";
1953
+ if (el.options && el.options[0]) {
1954
+ isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
1955
+ }
1956
+ el = null;
1957
+ return isBuggy;
1958
+ })();
1959
+
1960
+ var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
1961
+ try {
1962
+ var el = document.createElement("table");
1963
+ if (el && el.tBodies) {
1964
+ el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
1965
+ var isBuggy = typeof el.tBodies[0] == "undefined";
1966
+ el = null;
1967
+ return isBuggy;
1968
+ }
1969
+ } catch (e) {
1970
+ return true;
1971
+ }
1972
+ })();
1973
+
1974
+ var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
1975
+ try {
1976
+ var el = document.createElement('div');
1977
+ el.innerHTML = "<link>";
1978
+ var isBuggy = (el.childNodes.length === 0);
1979
+ el = null;
1980
+ return isBuggy;
1981
+ } catch(e) {
1982
+ return true;
1983
+ }
1984
+ })();
1985
+
1986
+ var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
1987
+ TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
1988
+
1989
+ var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
1990
+ var s = document.createElement("script"),
1991
+ isBuggy = false;
1992
+ try {
1993
+ s.appendChild(document.createTextNode(""));
1994
+ isBuggy = !s.firstChild ||
1995
+ s.firstChild && s.firstChild.nodeType !== 3;
1996
+ } catch (e) {
1997
+ isBuggy = true;
1998
+ }
1999
+ s = null;
2000
+ return isBuggy;
2001
+ })();
2002
+
2003
+
2004
+ function update(element, content) {
2005
+ element = $(element);
2006
+ var purgeElement = Element._purgeElement;
2007
+
2008
+ var descendants = element.getElementsByTagName('*'),
2009
+ i = descendants.length;
2010
+ while (i--) purgeElement(descendants[i]);
2011
+
2012
+ if (content && content.toElement)
2013
+ content = content.toElement();
2014
+
2015
+ if (Object.isElement(content))
2016
+ return element.update().insert(content);
2017
+
2018
+ content = Object.toHTML(content);
2019
+
2020
+ var tagName = element.tagName.toUpperCase();
2021
+
2022
+ if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
2023
+ element.text = content;
2024
+ return element;
2025
+ }
2026
+
2027
+ if (ANY_INNERHTML_BUGGY) {
2028
+ if (tagName in Element._insertionTranslations.tags) {
2029
+ while (element.firstChild) {
2030
+ element.removeChild(element.firstChild);
2031
+ }
2032
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2033
+ .each(function(node) {
2034
+ element.appendChild(node)
2035
+ });
2036
+ } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
2037
+ while (element.firstChild) {
2038
+ element.removeChild(element.firstChild);
2039
+ }
2040
+ var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
2041
+ nodes.each(function(node) { element.appendChild(node) });
2042
+ }
2043
+ else {
2044
+ element.innerHTML = content.stripScripts();
2045
+ }
2046
+ }
2047
+ else {
2048
+ element.innerHTML = content.stripScripts();
2049
+ }
2050
+
2051
+ content.evalScripts.bind(content).defer();
2052
+ return element;
2053
+ }
2054
+
2055
+ return update;
2056
+ })(),
2057
+
2058
+ replace: function(element, content) {
2059
+ element = $(element);
2060
+ if (content && content.toElement) content = content.toElement();
2061
+ else if (!Object.isElement(content)) {
2062
+ content = Object.toHTML(content);
2063
+ var range = element.ownerDocument.createRange();
2064
+ range.selectNode(element);
2065
+ content.evalScripts.bind(content).defer();
2066
+ content = range.createContextualFragment(content.stripScripts());
2067
+ }
2068
+ element.parentNode.replaceChild(content, element);
2069
+ return element;
2070
+ },
2071
+
2072
+ insert: function(element, insertions) {
2073
+ element = $(element);
2074
+
2075
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
2076
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2077
+ insertions = {bottom:insertions};
2078
+
2079
+ var content, insert, tagName, childNodes;
2080
+
2081
+ for (var position in insertions) {
2082
+ content = insertions[position];
2083
+ position = position.toLowerCase();
2084
+ insert = Element._insertionTranslations[position];
2085
+
2086
+ if (content && content.toElement) content = content.toElement();
2087
+ if (Object.isElement(content)) {
2088
+ insert(element, content);
2089
+ continue;
2090
+ }
2091
+
2092
+ content = Object.toHTML(content);
2093
+
2094
+ tagName = ((position == 'before' || position == 'after')
2095
+ ? element.parentNode : element).tagName.toUpperCase();
2096
+
2097
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2098
+
2099
+ if (position == 'top' || position == 'after') childNodes.reverse();
2100
+ childNodes.each(insert.curry(element));
2101
+
2102
+ content.evalScripts.bind(content).defer();
2103
+ }
2104
+
2105
+ return element;
2106
+ },
2107
+
2108
+ wrap: function(element, wrapper, attributes) {
2109
+ element = $(element);
2110
+ if (Object.isElement(wrapper))
2111
+ $(wrapper).writeAttribute(attributes || { });
2112
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
2113
+ else wrapper = new Element('div', wrapper);
2114
+ if (element.parentNode)
2115
+ element.parentNode.replaceChild(wrapper, element);
2116
+ wrapper.appendChild(element);
2117
+ return wrapper;
2118
+ },
2119
+
2120
+ inspect: function(element) {
2121
+ element = $(element);
2122
+ var result = '<' + element.tagName.toLowerCase();
2123
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
2124
+ var property = pair.first(),
2125
+ attribute = pair.last(),
2126
+ value = (element[property] || '').toString();
2127
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
2128
+ });
2129
+ return result + '>';
2130
+ },
2131
+
2132
+ recursivelyCollect: function(element, property, maximumLength) {
2133
+ element = $(element);
2134
+ maximumLength = maximumLength || -1;
2135
+ var elements = [];
2136
+
2137
+ while (element = element[property]) {
2138
+ if (element.nodeType == 1)
2139
+ elements.push(Element.extend(element));
2140
+ if (elements.length == maximumLength)
2141
+ break;
2142
+ }
2143
+
2144
+ return elements;
2145
+ },
2146
+
2147
+ ancestors: function(element) {
2148
+ return Element.recursivelyCollect(element, 'parentNode');
2149
+ },
2150
+
2151
+ descendants: function(element) {
2152
+ return Element.select(element, "*");
2153
+ },
2154
+
2155
+ firstDescendant: function(element) {
2156
+ element = $(element).firstChild;
2157
+ while (element && element.nodeType != 1) element = element.nextSibling;
2158
+ return $(element);
2159
+ },
2160
+
2161
+ immediateDescendants: function(element) {
2162
+ var results = [], child = $(element).firstChild;
2163
+ while (child) {
2164
+ if (child.nodeType === 1) {
2165
+ results.push(Element.extend(child));
2166
+ }
2167
+ child = child.nextSibling;
2168
+ }
2169
+ return results;
2170
+ },
2171
+
2172
+ previousSiblings: function(element, maximumLength) {
2173
+ return Element.recursivelyCollect(element, 'previousSibling');
2174
+ },
2175
+
2176
+ nextSiblings: function(element) {
2177
+ return Element.recursivelyCollect(element, 'nextSibling');
2178
+ },
2179
+
2180
+ siblings: function(element) {
2181
+ element = $(element);
2182
+ return Element.previousSiblings(element).reverse()
2183
+ .concat(Element.nextSiblings(element));
2184
+ },
2185
+
2186
+ match: function(element, selector) {
2187
+ element = $(element);
2188
+ if (Object.isString(selector))
2189
+ return Prototype.Selector.match(element, selector);
2190
+ return selector.match(element);
2191
+ },
2192
+
2193
+ up: function(element, expression, index) {
2194
+ element = $(element);
2195
+ if (arguments.length == 1) return $(element.parentNode);
2196
+ var ancestors = Element.ancestors(element);
2197
+ return Object.isNumber(expression) ? ancestors[expression] :
2198
+ Prototype.Selector.find(ancestors, expression, index);
2199
+ },
2200
+
2201
+ down: function(element, expression, index) {
2202
+ element = $(element);
2203
+ if (arguments.length == 1) return Element.firstDescendant(element);
2204
+ return Object.isNumber(expression) ? Element.descendants(element)[expression] :
2205
+ Element.select(element, expression)[index || 0];
2206
+ },
2207
+
2208
+ previous: function(element, expression, index) {
2209
+ element = $(element);
2210
+ if (Object.isNumber(expression)) index = expression, expression = false;
2211
+ if (!Object.isNumber(index)) index = 0;
2212
+
2213
+ if (expression) {
2214
+ return Prototype.Selector.find(element.previousSiblings(), expression, index);
2215
+ } else {
2216
+ return element.recursivelyCollect("previousSibling", index + 1)[index];
2217
+ }
2218
+ },
2219
+
2220
+ next: function(element, expression, index) {
2221
+ element = $(element);
2222
+ if (Object.isNumber(expression)) index = expression, expression = false;
2223
+ if (!Object.isNumber(index)) index = 0;
2224
+
2225
+ if (expression) {
2226
+ return Prototype.Selector.find(element.nextSiblings(), expression, index);
2227
+ } else {
2228
+ var maximumLength = Object.isNumber(index) ? index + 1 : 1;
2229
+ return element.recursivelyCollect("nextSibling", index + 1)[index];
2230
+ }
2231
+ },
2232
+
2233
+
2234
+ select: function(element) {
2235
+ element = $(element);
2236
+ var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2237
+ return Prototype.Selector.select(expressions, element);
2238
+ },
2239
+
2240
+ adjacent: function(element) {
2241
+ element = $(element);
2242
+ var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2243
+ return Prototype.Selector.select(expressions, element.parentNode).without(element);
2244
+ },
2245
+
2246
+ identify: function(element) {
2247
+ element = $(element);
2248
+ var id = Element.readAttribute(element, 'id');
2249
+ if (id) return id;
2250
+ do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
2251
+ Element.writeAttribute(element, 'id', id);
2252
+ return id;
2253
+ },
2254
+
2255
+ readAttribute: function(element, name) {
2256
+ element = $(element);
2257
+ if (Prototype.Browser.IE) {
2258
+ var t = Element._attributeTranslations.read;
2259
+ if (t.values[name]) return t.values[name](element, name);
2260
+ if (t.names[name]) name = t.names[name];
2261
+ if (name.include(':')) {
2262
+ return (!element.attributes || !element.attributes[name]) ? null :
2263
+ element.attributes[name].value;
2264
+ }
2265
+ }
2266
+ return element.getAttribute(name);
2267
+ },
2268
+
2269
+ writeAttribute: function(element, name, value) {
2270
+ element = $(element);
2271
+ var attributes = { }, t = Element._attributeTranslations.write;
2272
+
2273
+ if (typeof name == 'object') attributes = name;
2274
+ else attributes[name] = Object.isUndefined(value) ? true : value;
2275
+
2276
+ for (var attr in attributes) {
2277
+ name = t.names[attr] || attr;
2278
+ value = attributes[attr];
2279
+ if (t.values[attr]) name = t.values[attr](element, value);
2280
+ if (value === false || value === null)
2281
+ element.removeAttribute(name);
2282
+ else if (value === true)
2283
+ element.setAttribute(name, name);
2284
+ else element.setAttribute(name, value);
2285
+ }
2286
+ return element;
2287
+ },
2288
+
2289
+ getHeight: function(element) {
2290
+ return Element.getDimensions(element).height;
2291
+ },
2292
+
2293
+ getWidth: function(element) {
2294
+ return Element.getDimensions(element).width;
2295
+ },
2296
+
2297
+ classNames: function(element) {
2298
+ return new Element.ClassNames(element);
2299
+ },
2300
+
2301
+ hasClassName: function(element, className) {
2302
+ if (!(element = $(element))) return;
2303
+ var elementClassName = element.className;
2304
+ return (elementClassName.length > 0 && (elementClassName == className ||
2305
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
2306
+ },
2307
+
2308
+ addClassName: function(element, className) {
2309
+ if (!(element = $(element))) return;
2310
+ if (!Element.hasClassName(element, className))
2311
+ element.className += (element.className ? ' ' : '') + className;
2312
+ return element;
2313
+ },
2314
+
2315
+ removeClassName: function(element, className) {
2316
+ if (!(element = $(element))) return;
2317
+ element.className = element.className.replace(
2318
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
2319
+ return element;
2320
+ },
2321
+
2322
+ toggleClassName: function(element, className) {
2323
+ if (!(element = $(element))) return;
2324
+ return Element[Element.hasClassName(element, className) ?
2325
+ 'removeClassName' : 'addClassName'](element, className);
2326
+ },
2327
+
2328
+ cleanWhitespace: function(element) {
2329
+ element = $(element);
2330
+ var node = element.firstChild;
2331
+ while (node) {
2332
+ var nextNode = node.nextSibling;
2333
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
2334
+ element.removeChild(node);
2335
+ node = nextNode;
2336
+ }
2337
+ return element;
2338
+ },
2339
+
2340
+ empty: function(element) {
2341
+ return $(element).innerHTML.blank();
2342
+ },
2343
+
2344
+ descendantOf: function(element, ancestor) {
2345
+ element = $(element), ancestor = $(ancestor);
2346
+
2347
+ if (element.compareDocumentPosition)
2348
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
2349
+
2350
+ if (ancestor.contains)
2351
+ return ancestor.contains(element) && ancestor !== element;
2352
+
2353
+ while (element = element.parentNode)
2354
+ if (element == ancestor) return true;
2355
+
2356
+ return false;
2357
+ },
2358
+
2359
+ scrollTo: function(element) {
2360
+ element = $(element);
2361
+ var pos = Element.cumulativeOffset(element);
2362
+ window.scrollTo(pos[0], pos[1]);
2363
+ return element;
2364
+ },
2365
+
2366
+ getStyle: function(element, style) {
2367
+ element = $(element);
2368
+ style = style == 'float' ? 'cssFloat' : style.camelize();
2369
+ var value = element.style[style];
2370
+ if (!value || value == 'auto') {
2371
+ var css = document.defaultView.getComputedStyle(element, null);
2372
+ value = css ? css[style] : null;
2373
+ }
2374
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
2375
+ return value == 'auto' ? null : value;
2376
+ },
2377
+
2378
+ getOpacity: function(element) {
2379
+ return $(element).getStyle('opacity');
2380
+ },
2381
+
2382
+ setStyle: function(element, styles) {
2383
+ element = $(element);
2384
+ var elementStyle = element.style, match;
2385
+ if (Object.isString(styles)) {
2386
+ element.style.cssText += ';' + styles;
2387
+ return styles.include('opacity') ?
2388
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
2389
+ }
2390
+ for (var property in styles)
2391
+ if (property == 'opacity') element.setOpacity(styles[property]);
2392
+ else
2393
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
2394
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
2395
+ property] = styles[property];
2396
+
2397
+ return element;
2398
+ },
2399
+
2400
+ setOpacity: function(element, value) {
2401
+ element = $(element);
2402
+ element.style.opacity = (value == 1 || value === '') ? '' :
2403
+ (value < 0.00001) ? 0 : value;
2404
+ return element;
2405
+ },
2406
+
2407
+ makePositioned: function(element) {
2408
+ element = $(element);
2409
+ var pos = Element.getStyle(element, 'position');
2410
+ if (pos == 'static' || !pos) {
2411
+ element._madePositioned = true;
2412
+ element.style.position = 'relative';
2413
+ if (Prototype.Browser.Opera) {
2414
+ element.style.top = 0;
2415
+ element.style.left = 0;
2416
+ }
2417
+ }
2418
+ return element;
2419
+ },
2420
+
2421
+ undoPositioned: function(element) {
2422
+ element = $(element);
2423
+ if (element._madePositioned) {
2424
+ element._madePositioned = undefined;
2425
+ element.style.position =
2426
+ element.style.top =
2427
+ element.style.left =
2428
+ element.style.bottom =
2429
+ element.style.right = '';
2430
+ }
2431
+ return element;
2432
+ },
2433
+
2434
+ makeClipping: function(element) {
2435
+ element = $(element);
2436
+ if (element._overflow) return element;
2437
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
2438
+ if (element._overflow !== 'hidden')
2439
+ element.style.overflow = 'hidden';
2440
+ return element;
2441
+ },
2442
+
2443
+ undoClipping: function(element) {
2444
+ element = $(element);
2445
+ if (!element._overflow) return element;
2446
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2447
+ element._overflow = null;
2448
+ return element;
2449
+ },
2450
+
2451
+ clonePosition: function(element, source) {
2452
+ var options = Object.extend({
2453
+ setLeft: true,
2454
+ setTop: true,
2455
+ setWidth: true,
2456
+ setHeight: true,
2457
+ offsetTop: 0,
2458
+ offsetLeft: 0
2459
+ }, arguments[2] || { });
2460
+
2461
+ source = $(source);
2462
+ var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
2463
+
2464
+ element = $(element);
2465
+
2466
+ if (Element.getStyle(element, 'position') == 'absolute') {
2467
+ parent = Element.getOffsetParent(element);
2468
+ delta = Element.viewportOffset(parent);
2469
+ }
2470
+
2471
+ if (parent == document.body) {
2472
+ delta[0] -= document.body.offsetLeft;
2473
+ delta[1] -= document.body.offsetTop;
2474
+ }
2475
+
2476
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2477
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2478
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
2479
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2480
+ return element;
2481
+ }
2482
+ };
2483
+
2484
+ Object.extend(Element.Methods, {
2485
+ getElementsBySelector: Element.Methods.select,
2486
+
2487
+ childElements: Element.Methods.immediateDescendants
2488
+ });
2489
+
2490
+ Element._attributeTranslations = {
2491
+ write: {
2492
+ names: {
2493
+ className: 'class',
2494
+ htmlFor: 'for'
2495
+ },
2496
+ values: { }
2497
+ }
2498
+ };
2499
+
2500
+ if (Prototype.Browser.Opera) {
2501
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2502
+ function(proceed, element, style) {
2503
+ switch (style) {
2504
+ case 'height': case 'width':
2505
+ if (!Element.visible(element)) return null;
2506
+
2507
+ var dim = parseInt(proceed(element, style), 10);
2508
+
2509
+ if (dim !== element['offset' + style.capitalize()])
2510
+ return dim + 'px';
2511
+
2512
+ var properties;
2513
+ if (style === 'height') {
2514
+ properties = ['border-top-width', 'padding-top',
2515
+ 'padding-bottom', 'border-bottom-width'];
2516
+ }
2517
+ else {
2518
+ properties = ['border-left-width', 'padding-left',
2519
+ 'padding-right', 'border-right-width'];
2520
+ }
2521
+ return properties.inject(dim, function(memo, property) {
2522
+ var val = proceed(element, property);
2523
+ return val === null ? memo : memo - parseInt(val, 10);
2524
+ }) + 'px';
2525
+ default: return proceed(element, style);
2526
+ }
2527
+ }
2528
+ );
2529
+
2530
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2531
+ function(proceed, element, attribute) {
2532
+ if (attribute === 'title') return element.title;
2533
+ return proceed(element, attribute);
2534
+ }
2535
+ );
2536
+ }
2537
+
2538
+ else if (Prototype.Browser.IE) {
2539
+ Element.Methods.getStyle = function(element, style) {
2540
+ element = $(element);
2541
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2542
+ var value = element.style[style];
2543
+ if (!value && element.currentStyle) value = element.currentStyle[style];
2544
+
2545
+ if (style == 'opacity') {
2546
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2547
+ if (value[1]) return parseFloat(value[1]) / 100;
2548
+ return 1.0;
2549
+ }
2550
+
2551
+ if (value == 'auto') {
2552
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2553
+ return element['offset' + style.capitalize()] + 'px';
2554
+ return null;
2555
+ }
2556
+ return value;
2557
+ };
2558
+
2559
+ Element.Methods.setOpacity = function(element, value) {
2560
+ function stripAlpha(filter){
2561
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
2562
+ }
2563
+ element = $(element);
2564
+ var currentStyle = element.currentStyle;
2565
+ if ((currentStyle && !currentStyle.hasLayout) ||
2566
+ (!currentStyle && element.style.zoom == 'normal'))
2567
+ element.style.zoom = 1;
2568
+
2569
+ var filter = element.getStyle('filter'), style = element.style;
2570
+ if (value == 1 || value === '') {
2571
+ (filter = stripAlpha(filter)) ?
2572
+ style.filter = filter : style.removeAttribute('filter');
2573
+ return element;
2574
+ } else if (value < 0.00001) value = 0;
2575
+ style.filter = stripAlpha(filter) +
2576
+ 'alpha(opacity=' + (value * 100) + ')';
2577
+ return element;
2578
+ };
2579
+
2580
+ Element._attributeTranslations = (function(){
2581
+
2582
+ var classProp = 'className',
2583
+ forProp = 'for',
2584
+ el = document.createElement('div');
2585
+
2586
+ el.setAttribute(classProp, 'x');
2587
+
2588
+ if (el.className !== 'x') {
2589
+ el.setAttribute('class', 'x');
2590
+ if (el.className === 'x') {
2591
+ classProp = 'class';
2592
+ }
2593
+ }
2594
+ el = null;
2595
+
2596
+ el = document.createElement('label');
2597
+ el.setAttribute(forProp, 'x');
2598
+ if (el.htmlFor !== 'x') {
2599
+ el.setAttribute('htmlFor', 'x');
2600
+ if (el.htmlFor === 'x') {
2601
+ forProp = 'htmlFor';
2602
+ }
2603
+ }
2604
+ el = null;
2605
+
2606
+ return {
2607
+ read: {
2608
+ names: {
2609
+ 'class': classProp,
2610
+ 'className': classProp,
2611
+ 'for': forProp,
2612
+ 'htmlFor': forProp
2613
+ },
2614
+ values: {
2615
+ _getAttr: function(element, attribute) {
2616
+ return element.getAttribute(attribute);
2617
+ },
2618
+ _getAttr2: function(element, attribute) {
2619
+ return element.getAttribute(attribute, 2);
2620
+ },
2621
+ _getAttrNode: function(element, attribute) {
2622
+ var node = element.getAttributeNode(attribute);
2623
+ return node ? node.value : "";
2624
+ },
2625
+ _getEv: (function(){
2626
+
2627
+ var el = document.createElement('div'), f;
2628
+ el.onclick = Prototype.emptyFunction;
2629
+ var value = el.getAttribute('onclick');
2630
+
2631
+ if (String(value).indexOf('{') > -1) {
2632
+ f = function(element, attribute) {
2633
+ attribute = element.getAttribute(attribute);
2634
+ if (!attribute) return null;
2635
+ attribute = attribute.toString();
2636
+ attribute = attribute.split('{')[1];
2637
+ attribute = attribute.split('}')[0];
2638
+ return attribute.strip();
2639
+ };
2640
+ }
2641
+ else if (value === '') {
2642
+ f = function(element, attribute) {
2643
+ attribute = element.getAttribute(attribute);
2644
+ if (!attribute) return null;
2645
+ return attribute.strip();
2646
+ };
2647
+ }
2648
+ el = null;
2649
+ return f;
2650
+ })(),
2651
+ _flag: function(element, attribute) {
2652
+ return $(element).hasAttribute(attribute) ? attribute : null;
2653
+ },
2654
+ style: function(element) {
2655
+ return element.style.cssText.toLowerCase();
2656
+ },
2657
+ title: function(element) {
2658
+ return element.title;
2659
+ }
2660
+ }
2661
+ }
2662
+ }
2663
+ })();
2664
+
2665
+ Element._attributeTranslations.write = {
2666
+ names: Object.extend({
2667
+ cellpadding: 'cellPadding',
2668
+ cellspacing: 'cellSpacing'
2669
+ }, Element._attributeTranslations.read.names),
2670
+ values: {
2671
+ checked: function(element, value) {
2672
+ element.checked = !!value;
2673
+ },
2674
+
2675
+ style: function(element, value) {
2676
+ element.style.cssText = value ? value : '';
2677
+ }
2678
+ }
2679
+ };
2680
+
2681
+ Element._attributeTranslations.has = {};
2682
+
2683
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2684
+ 'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
2685
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2686
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2687
+ });
2688
+
2689
+ (function(v) {
2690
+ Object.extend(v, {
2691
+ href: v._getAttr2,
2692
+ src: v._getAttr2,
2693
+ type: v._getAttr,
2694
+ action: v._getAttrNode,
2695
+ disabled: v._flag,
2696
+ checked: v._flag,
2697
+ readonly: v._flag,
2698
+ multiple: v._flag,
2699
+ onload: v._getEv,
2700
+ onunload: v._getEv,
2701
+ onclick: v._getEv,
2702
+ ondblclick: v._getEv,
2703
+ onmousedown: v._getEv,
2704
+ onmouseup: v._getEv,
2705
+ onmouseover: v._getEv,
2706
+ onmousemove: v._getEv,
2707
+ onmouseout: v._getEv,
2708
+ onfocus: v._getEv,
2709
+ onblur: v._getEv,
2710
+ onkeypress: v._getEv,
2711
+ onkeydown: v._getEv,
2712
+ onkeyup: v._getEv,
2713
+ onsubmit: v._getEv,
2714
+ onreset: v._getEv,
2715
+ onselect: v._getEv,
2716
+ onchange: v._getEv
2717
+ });
2718
+ })(Element._attributeTranslations.read.values);
2719
+
2720
+ if (Prototype.BrowserFeatures.ElementExtensions) {
2721
+ (function() {
2722
+ function _descendants(element) {
2723
+ var nodes = element.getElementsByTagName('*'), results = [];
2724
+ for (var i = 0, node; node = nodes[i]; i++)
2725
+ if (node.tagName !== "!") // Filter out comment nodes.
2726
+ results.push(node);
2727
+ return results;
2728
+ }
2729
+
2730
+ Element.Methods.down = function(element, expression, index) {
2731
+ element = $(element);
2732
+ if (arguments.length == 1) return element.firstDescendant();
2733
+ return Object.isNumber(expression) ? _descendants(element)[expression] :
2734
+ Element.select(element, expression)[index || 0];
2735
+ }
2736
+ })();
2737
+ }
2738
+
2739
+ }
2740
+
2741
+ else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2742
+ Element.Methods.setOpacity = function(element, value) {
2743
+ element = $(element);
2744
+ element.style.opacity = (value == 1) ? 0.999999 :
2745
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
2746
+ return element;
2747
+ };
2748
+ }
2749
+
2750
+ else if (Prototype.Browser.WebKit) {
2751
+ Element.Methods.setOpacity = function(element, value) {
2752
+ element = $(element);
2753
+ element.style.opacity = (value == 1 || value === '') ? '' :
2754
+ (value < 0.00001) ? 0 : value;
2755
+
2756
+ if (value == 1)
2757
+ if (element.tagName.toUpperCase() == 'IMG' && element.width) {
2758
+ element.width++; element.width--;
2759
+ } else try {
2760
+ var n = document.createTextNode(' ');
2761
+ element.appendChild(n);
2762
+ element.removeChild(n);
2763
+ } catch (e) { }
2764
+
2765
+ return element;
2766
+ };
2767
+ }
2768
+
2769
+ if ('outerHTML' in document.documentElement) {
2770
+ Element.Methods.replace = function(element, content) {
2771
+ element = $(element);
2772
+
2773
+ if (content && content.toElement) content = content.toElement();
2774
+ if (Object.isElement(content)) {
2775
+ element.parentNode.replaceChild(content, element);
2776
+ return element;
2777
+ }
2778
+
2779
+ content = Object.toHTML(content);
2780
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2781
+
2782
+ if (Element._insertionTranslations.tags[tagName]) {
2783
+ var nextSibling = element.next(),
2784
+ fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2785
+ parent.removeChild(element);
2786
+ if (nextSibling)
2787
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2788
+ else
2789
+ fragments.each(function(node) { parent.appendChild(node) });
2790
+ }
2791
+ else element.outerHTML = content.stripScripts();
2792
+
2793
+ content.evalScripts.bind(content).defer();
2794
+ return element;
2795
+ };
2796
+ }
2797
+
2798
+ Element._returnOffset = function(l, t) {
2799
+ var result = [l, t];
2800
+ result.left = l;
2801
+ result.top = t;
2802
+ return result;
2803
+ };
2804
+
2805
+ Element._getContentFromAnonymousElement = function(tagName, html, force) {
2806
+ var div = new Element('div'),
2807
+ t = Element._insertionTranslations.tags[tagName];
2808
+
2809
+ var workaround = false;
2810
+ if (t) workaround = true;
2811
+ else if (force) {
2812
+ workaround = true;
2813
+ t = ['', '', 0];
2814
+ }
2815
+
2816
+ if (workaround) {
2817
+ div.innerHTML = '&nbsp;' + t[0] + html + t[1];
2818
+ div.removeChild(div.firstChild);
2819
+ for (var i = t[2]; i--; ) {
2820
+ div = div.firstChild;
2821
+ }
2822
+ }
2823
+ else {
2824
+ div.innerHTML = html;
2825
+ }
2826
+ return $A(div.childNodes);
2827
+ };
2828
+
2829
+ Element._insertionTranslations = {
2830
+ before: function(element, node) {
2831
+ element.parentNode.insertBefore(node, element);
2832
+ },
2833
+ top: function(element, node) {
2834
+ element.insertBefore(node, element.firstChild);
2835
+ },
2836
+ bottom: function(element, node) {
2837
+ element.appendChild(node);
2838
+ },
2839
+ after: function(element, node) {
2840
+ element.parentNode.insertBefore(node, element.nextSibling);
2841
+ },
2842
+ tags: {
2843
+ TABLE: ['<table>', '</table>', 1],
2844
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
2845
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2846
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2847
+ SELECT: ['<select>', '</select>', 1]
2848
+ }
2849
+ };
2850
+
2851
+ (function() {
2852
+ var tags = Element._insertionTranslations.tags;
2853
+ Object.extend(tags, {
2854
+ THEAD: tags.TBODY,
2855
+ TFOOT: tags.TBODY,
2856
+ TH: tags.TD
2857
+ });
2858
+ })();
2859
+
2860
+ Element.Methods.Simulated = {
2861
+ hasAttribute: function(element, attribute) {
2862
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
2863
+ var node = $(element).getAttributeNode(attribute);
2864
+ return !!(node && node.specified);
2865
+ }
2866
+ };
2867
+
2868
+ Element.Methods.ByTag = { };
2869
+
2870
+ Object.extend(Element, Element.Methods);
2871
+
2872
+ (function(div) {
2873
+
2874
+ if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
2875
+ window.HTMLElement = { };
2876
+ window.HTMLElement.prototype = div['__proto__'];
2877
+ Prototype.BrowserFeatures.ElementExtensions = true;
2878
+ }
2879
+
2880
+ div = null;
2881
+
2882
+ })(document.createElement('div'));
2883
+
2884
+ Element.extend = (function() {
2885
+
2886
+ function checkDeficiency(tagName) {
2887
+ if (typeof window.Element != 'undefined') {
2888
+ var proto = window.Element.prototype;
2889
+ if (proto) {
2890
+ var id = '_' + (Math.random()+'').slice(2),
2891
+ el = document.createElement(tagName);
2892
+ proto[id] = 'x';
2893
+ var isBuggy = (el[id] !== 'x');
2894
+ delete proto[id];
2895
+ el = null;
2896
+ return isBuggy;
2897
+ }
2898
+ }
2899
+ return false;
2900
+ }
2901
+
2902
+ function extendElementWith(element, methods) {
2903
+ for (var property in methods) {
2904
+ var value = methods[property];
2905
+ if (Object.isFunction(value) && !(property in element))
2906
+ element[property] = value.methodize();
2907
+ }
2908
+ }
2909
+
2910
+ var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
2911
+
2912
+ if (Prototype.BrowserFeatures.SpecificElementExtensions) {
2913
+ if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
2914
+ return function(element) {
2915
+ if (element && typeof element._extendedByPrototype == 'undefined') {
2916
+ var t = element.tagName;
2917
+ if (t && (/^(?:object|applet|embed)$/i.test(t))) {
2918
+ extendElementWith(element, Element.Methods);
2919
+ extendElementWith(element, Element.Methods.Simulated);
2920
+ extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
2921
+ }
2922
+ }
2923
+ return element;
2924
+ }
2925
+ }
2926
+ return Prototype.K;
2927
+ }
2928
+
2929
+ var Methods = { }, ByTag = Element.Methods.ByTag;
2930
+
2931
+ var extend = Object.extend(function(element) {
2932
+ if (!element || typeof element._extendedByPrototype != 'undefined' ||
2933
+ element.nodeType != 1 || element == window) return element;
2934
+
2935
+ var methods = Object.clone(Methods),
2936
+ tagName = element.tagName.toUpperCase();
2937
+
2938
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2939
+
2940
+ extendElementWith(element, methods);
2941
+
2942
+ element._extendedByPrototype = Prototype.emptyFunction;
2943
+ return element;
2944
+
2945
+ }, {
2946
+ refresh: function() {
2947
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
2948
+ Object.extend(Methods, Element.Methods);
2949
+ Object.extend(Methods, Element.Methods.Simulated);
2950
+ }
2951
+ }
2952
+ });
2953
+
2954
+ extend.refresh();
2955
+ return extend;
2956
+ })();
2957
+
2958
+ if (document.documentElement.hasAttribute) {
2959
+ Element.hasAttribute = function(element, attribute) {
2960
+ return element.hasAttribute(attribute);
2961
+ };
2962
+ }
2963
+ else {
2964
+ Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
2965
+ }
2966
+
2967
+ Element.addMethods = function(methods) {
2968
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2969
+
2970
+ if (!methods) {
2971
+ Object.extend(Form, Form.Methods);
2972
+ Object.extend(Form.Element, Form.Element.Methods);
2973
+ Object.extend(Element.Methods.ByTag, {
2974
+ "FORM": Object.clone(Form.Methods),
2975
+ "INPUT": Object.clone(Form.Element.Methods),
2976
+ "SELECT": Object.clone(Form.Element.Methods),
2977
+ "TEXTAREA": Object.clone(Form.Element.Methods),
2978
+ "BUTTON": Object.clone(Form.Element.Methods)
2979
+ });
2980
+ }
2981
+
2982
+ if (arguments.length == 2) {
2983
+ var tagName = methods;
2984
+ methods = arguments[1];
2985
+ }
2986
+
2987
+ if (!tagName) Object.extend(Element.Methods, methods || { });
2988
+ else {
2989
+ if (Object.isArray(tagName)) tagName.each(extend);
2990
+ else extend(tagName);
2991
+ }
2992
+
2993
+ function extend(tagName) {
2994
+ tagName = tagName.toUpperCase();
2995
+ if (!Element.Methods.ByTag[tagName])
2996
+ Element.Methods.ByTag[tagName] = { };
2997
+ Object.extend(Element.Methods.ByTag[tagName], methods);
2998
+ }
2999
+
3000
+ function copy(methods, destination, onlyIfAbsent) {
3001
+ onlyIfAbsent = onlyIfAbsent || false;
3002
+ for (var property in methods) {
3003
+ var value = methods[property];
3004
+ if (!Object.isFunction(value)) continue;
3005
+ if (!onlyIfAbsent || !(property in destination))
3006
+ destination[property] = value.methodize();
3007
+ }
3008
+ }
3009
+
3010
+ function findDOMClass(tagName) {
3011
+ var klass;
3012
+ var trans = {
3013
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3014
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3015
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3016
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3017
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3018
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3019
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3020
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3021
+ "FrameSet", "IFRAME": "IFrame"
3022
+ };
3023
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3024
+ if (window[klass]) return window[klass];
3025
+ klass = 'HTML' + tagName + 'Element';
3026
+ if (window[klass]) return window[klass];
3027
+ klass = 'HTML' + tagName.capitalize() + 'Element';
3028
+ if (window[klass]) return window[klass];
3029
+
3030
+ var element = document.createElement(tagName),
3031
+ proto = element['__proto__'] || element.constructor.prototype;
3032
+
3033
+ element = null;
3034
+ return proto;
3035
+ }
3036
+
3037
+ var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
3038
+ Element.prototype;
3039
+
3040
+ if (F.ElementExtensions) {
3041
+ copy(Element.Methods, elementPrototype);
3042
+ copy(Element.Methods.Simulated, elementPrototype, true);
3043
+ }
3044
+
3045
+ if (F.SpecificElementExtensions) {
3046
+ for (var tag in Element.Methods.ByTag) {
3047
+ var klass = findDOMClass(tag);
3048
+ if (Object.isUndefined(klass)) continue;
3049
+ copy(T[tag], klass.prototype);
3050
+ }
3051
+ }
3052
+
3053
+ Object.extend(Element, Element.Methods);
3054
+ delete Element.ByTag;
3055
+
3056
+ if (Element.extend.refresh) Element.extend.refresh();
3057
+ Element.cache = { };
3058
+ };
3059
+
3060
+
3061
+ document.viewport = {
3062
+
3063
+ getDimensions: function() {
3064
+ return { width: this.getWidth(), height: this.getHeight() };
3065
+ },
3066
+
3067
+ getScrollOffsets: function() {
3068
+ return Element._returnOffset(
3069
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
3070
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
3071
+ }
3072
+ };
3073
+
3074
+ (function(viewport) {
3075
+ var B = Prototype.Browser, doc = document, element, property = {};
3076
+
3077
+ function getRootElement() {
3078
+ if (B.WebKit && !doc.evaluate)
3079
+ return document;
3080
+
3081
+ if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
3082
+ return document.body;
3083
+
3084
+ return document.documentElement;
3085
+ }
3086
+
3087
+ function define(D) {
3088
+ if (!element) element = getRootElement();
3089
+
3090
+ property[D] = 'client' + D;
3091
+
3092
+ viewport['get' + D] = function() { return element[property[D]] };
3093
+ return viewport['get' + D]();
3094
+ }
3095
+
3096
+ viewport.getWidth = define.curry('Width');
3097
+
3098
+ viewport.getHeight = define.curry('Height');
3099
+ })(document.viewport);
3100
+
3101
+
3102
+ Element.Storage = {
3103
+ UID: 1
3104
+ };
3105
+
3106
+ Element.addMethods({
3107
+ getStorage: function(element) {
3108
+ if (!(element = $(element))) return;
3109
+
3110
+ var uid;
3111
+ if (element === window) {
3112
+ uid = 0;
3113
+ } else {
3114
+ if (typeof element._prototypeUID === "undefined")
3115
+ element._prototypeUID = Element.Storage.UID++;
3116
+ uid = element._prototypeUID;
3117
+ }
3118
+
3119
+ if (!Element.Storage[uid])
3120
+ Element.Storage[uid] = $H();
3121
+
3122
+ return Element.Storage[uid];
3123
+ },
3124
+
3125
+ store: function(element, key, value) {
3126
+ if (!(element = $(element))) return;
3127
+
3128
+ if (arguments.length === 2) {
3129
+ Element.getStorage(element).update(key);
3130
+ } else {
3131
+ Element.getStorage(element).set(key, value);
3132
+ }
3133
+
3134
+ return element;
3135
+ },
3136
+
3137
+ retrieve: function(element, key, defaultValue) {
3138
+ if (!(element = $(element))) return;
3139
+ var hash = Element.getStorage(element), value = hash.get(key);
3140
+
3141
+ if (Object.isUndefined(value)) {
3142
+ hash.set(key, defaultValue);
3143
+ value = defaultValue;
3144
+ }
3145
+
3146
+ return value;
3147
+ },
3148
+
3149
+ clone: function(element, deep) {
3150
+ if (!(element = $(element))) return;
3151
+ var clone = element.cloneNode(deep);
3152
+ clone._prototypeUID = void 0;
3153
+ if (deep) {
3154
+ var descendants = Element.select(clone, '*'),
3155
+ i = descendants.length;
3156
+ while (i--) {
3157
+ descendants[i]._prototypeUID = void 0;
3158
+ }
3159
+ }
3160
+ return Element.extend(clone);
3161
+ },
3162
+
3163
+ purge: function(element) {
3164
+ if (!(element = $(element))) return;
3165
+ var purgeElement = Element._purgeElement;
3166
+
3167
+ purgeElement(element);
3168
+
3169
+ var descendants = element.getElementsByTagName('*'),
3170
+ i = descendants.length;
3171
+
3172
+ while (i--) purgeElement(descendants[i]);
3173
+
3174
+ return null;
3175
+ }
3176
+ });
3177
+
3178
+ (function() {
3179
+
3180
+ function toDecimal(pctString) {
3181
+ var match = pctString.match(/^(\d+)%?$/i);
3182
+ if (!match) return null;
3183
+ return (Number(match[1]) / 100);
3184
+ }
3185
+
3186
+ function getPixelValue(value, property, context) {
3187
+ var element = null;
3188
+ if (Object.isElement(value)) {
3189
+ element = value;
3190
+ value = element.getStyle(property);
3191
+ }
3192
+
3193
+ if (value === null) {
3194
+ return null;
3195
+ }
3196
+
3197
+ if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
3198
+ return window.parseFloat(value);
3199
+ }
3200
+
3201
+ var isPercentage = value.include('%'), isViewport = (context === document.viewport);
3202
+
3203
+ if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
3204
+ var style = element.style.left, rStyle = element.runtimeStyle.left;
3205
+ element.runtimeStyle.left = element.currentStyle.left;
3206
+ element.style.left = value || 0;
3207
+ value = element.style.pixelLeft;
3208
+ element.style.left = style;
3209
+ element.runtimeStyle.left = rStyle;
3210
+
3211
+ return value;
3212
+ }
3213
+
3214
+ if (element && isPercentage) {
3215
+ context = context || element.parentNode;
3216
+ var decimal = toDecimal(value);
3217
+ var whole = null;
3218
+ var position = element.getStyle('position');
3219
+
3220
+ var isHorizontal = property.include('left') || property.include('right') ||
3221
+ property.include('width');
3222
+
3223
+ var isVertical = property.include('top') || property.include('bottom') ||
3224
+ property.include('height');
3225
+
3226
+ if (context === document.viewport) {
3227
+ if (isHorizontal) {
3228
+ whole = document.viewport.getWidth();
3229
+ } else if (isVertical) {
3230
+ whole = document.viewport.getHeight();
3231
+ }
3232
+ } else {
3233
+ if (isHorizontal) {
3234
+ whole = $(context).measure('width');
3235
+ } else if (isVertical) {
3236
+ whole = $(context).measure('height');
3237
+ }
3238
+ }
3239
+
3240
+ return (whole === null) ? 0 : whole * decimal;
3241
+ }
3242
+
3243
+ return 0;
3244
+ }
3245
+
3246
+ function toCSSPixels(number) {
3247
+ if (Object.isString(number) && number.endsWith('px')) {
3248
+ return number;
3249
+ }
3250
+ return number + 'px';
3251
+ }
3252
+
3253
+ function isDisplayed(element) {
3254
+ var originalElement = element;
3255
+ while (element && element.parentNode) {
3256
+ var display = element.getStyle('display');
3257
+ if (display === 'none') {
3258
+ return false;
3259
+ }
3260
+ element = $(element.parentNode);
3261
+ }
3262
+ return true;
3263
+ }
3264
+
3265
+ var hasLayout = Prototype.K;
3266
+ if ('currentStyle' in document.documentElement) {
3267
+ hasLayout = function(element) {
3268
+ if (!element.currentStyle.hasLayout) {
3269
+ element.style.zoom = 1;
3270
+ }
3271
+ return element;
3272
+ };
3273
+ }
3274
+
3275
+ function cssNameFor(key) {
3276
+ if (key.include('border')) key = key + '-width';
3277
+ return key.camelize();
3278
+ }
3279
+
3280
+ Element.Layout = Class.create(Hash, {
3281
+ initialize: function($super, element, preCompute) {
3282
+ $super();
3283
+ this.element = $(element);
3284
+
3285
+ Element.Layout.PROPERTIES.each( function(property) {
3286
+ this._set(property, null);
3287
+ }, this);
3288
+
3289
+ if (preCompute) {
3290
+ this._preComputing = true;
3291
+ this._begin();
3292
+ Element.Layout.PROPERTIES.each( this._compute, this );
3293
+ this._end();
3294
+ this._preComputing = false;
3295
+ }
3296
+ },
3297
+
3298
+ _set: function(property, value) {
3299
+ return Hash.prototype.set.call(this, property, value);
3300
+ },
3301
+
3302
+ set: function(property, value) {
3303
+ throw "Properties of Element.Layout are read-only.";
3304
+ },
3305
+
3306
+ get: function($super, property) {
3307
+ var value = $super(property);
3308
+ return value === null ? this._compute(property) : value;
3309
+ },
3310
+
3311
+ _begin: function() {
3312
+ if (this._prepared) return;
3313
+
3314
+ var element = this.element;
3315
+ if (isDisplayed(element)) {
3316
+ this._prepared = true;
3317
+ return;
3318
+ }
3319
+
3320
+ var originalStyles = {
3321
+ position: element.style.position || '',
3322
+ width: element.style.width || '',
3323
+ visibility: element.style.visibility || '',
3324
+ display: element.style.display || ''
3325
+ };
3326
+
3327
+ element.store('prototype_original_styles', originalStyles);
3328
+
3329
+ var position = element.getStyle('position'),
3330
+ width = element.getStyle('width');
3331
+
3332
+ if (width === "0px" || width === null) {
3333
+ element.style.display = 'block';
3334
+ width = element.getStyle('width');
3335
+ }
3336
+
3337
+ var context = (position === 'fixed') ? document.viewport :
3338
+ element.parentNode;
3339
+
3340
+ element.setStyle({
3341
+ position: 'absolute',
3342
+ visibility: 'hidden',
3343
+ display: 'block'
3344
+ });
3345
+
3346
+ var positionedWidth = element.getStyle('width');
3347
+
3348
+ var newWidth;
3349
+ if (width && (positionedWidth === width)) {
3350
+ newWidth = getPixelValue(element, 'width', context);
3351
+ } else if (position === 'absolute' || position === 'fixed') {
3352
+ newWidth = getPixelValue(element, 'width', context);
3353
+ } else {
3354
+ var parent = element.parentNode, pLayout = $(parent).getLayout();
3355
+
3356
+ newWidth = pLayout.get('width') -
3357
+ this.get('margin-left') -
3358
+ this.get('border-left') -
3359
+ this.get('padding-left') -
3360
+ this.get('padding-right') -
3361
+ this.get('border-right') -
3362
+ this.get('margin-right');
3363
+ }
3364
+
3365
+ element.setStyle({ width: newWidth + 'px' });
3366
+
3367
+ this._prepared = true;
3368
+ },
3369
+
3370
+ _end: function() {
3371
+ var element = this.element;
3372
+ var originalStyles = element.retrieve('prototype_original_styles');
3373
+ element.store('prototype_original_styles', null);
3374
+ element.setStyle(originalStyles);
3375
+ this._prepared = false;
3376
+ },
3377
+
3378
+ _compute: function(property) {
3379
+ var COMPUTATIONS = Element.Layout.COMPUTATIONS;
3380
+ if (!(property in COMPUTATIONS)) {
3381
+ throw "Property not found.";
3382
+ }
3383
+
3384
+ return this._set(property, COMPUTATIONS[property].call(this, this.element));
3385
+ },
3386
+
3387
+ toObject: function() {
3388
+ var args = $A(arguments);
3389
+ var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3390
+ args.join(' ').split(' ');
3391
+ var obj = {};
3392
+ keys.each( function(key) {
3393
+ if (!Element.Layout.PROPERTIES.include(key)) return;
3394
+ var value = this.get(key);
3395
+ if (value != null) obj[key] = value;
3396
+ }, this);
3397
+ return obj;
3398
+ },
3399
+
3400
+ toHash: function() {
3401
+ var obj = this.toObject.apply(this, arguments);
3402
+ return new Hash(obj);
3403
+ },
3404
+
3405
+ toCSS: function() {
3406
+ var args = $A(arguments);
3407
+ var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3408
+ args.join(' ').split(' ');
3409
+ var css = {};
3410
+
3411
+ keys.each( function(key) {
3412
+ if (!Element.Layout.PROPERTIES.include(key)) return;
3413
+ if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
3414
+
3415
+ var value = this.get(key);
3416
+ if (value != null) css[cssNameFor(key)] = value + 'px';
3417
+ }, this);
3418
+ return css;
3419
+ },
3420
+
3421
+ inspect: function() {
3422
+ return "#<Element.Layout>";
3423
+ }
3424
+ });
3425
+
3426
+ Object.extend(Element.Layout, {
3427
+ PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
3428
+
3429
+ COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
3430
+
3431
+ COMPUTATIONS: {
3432
+ 'height': function(element) {
3433
+ if (!this._preComputing) this._begin();
3434
+
3435
+ var bHeight = this.get('border-box-height');
3436
+ if (bHeight <= 0) {
3437
+ if (!this._preComputing) this._end();
3438
+ return 0;
3439
+ }
3440
+
3441
+ var bTop = this.get('border-top'),
3442
+ bBottom = this.get('border-bottom');
3443
+
3444
+ var pTop = this.get('padding-top'),
3445
+ pBottom = this.get('padding-bottom');
3446
+
3447
+ if (!this._preComputing) this._end();
3448
+
3449
+ return bHeight - bTop - bBottom - pTop - pBottom;
3450
+ },
3451
+
3452
+ 'width': function(element) {
3453
+ if (!this._preComputing) this._begin();
3454
+
3455
+ var bWidth = this.get('border-box-width');
3456
+ if (bWidth <= 0) {
3457
+ if (!this._preComputing) this._end();
3458
+ return 0;
3459
+ }
3460
+
3461
+ var bLeft = this.get('border-left'),
3462
+ bRight = this.get('border-right');
3463
+
3464
+ var pLeft = this.get('padding-left'),
3465
+ pRight = this.get('padding-right');
3466
+
3467
+ if (!this._preComputing) this._end();
3468
+
3469
+ return bWidth - bLeft - bRight - pLeft - pRight;
3470
+ },
3471
+
3472
+ 'padding-box-height': function(element) {
3473
+ var height = this.get('height'),
3474
+ pTop = this.get('padding-top'),
3475
+ pBottom = this.get('padding-bottom');
3476
+
3477
+ return height + pTop + pBottom;
3478
+ },
3479
+
3480
+ 'padding-box-width': function(element) {
3481
+ var width = this.get('width'),
3482
+ pLeft = this.get('padding-left'),
3483
+ pRight = this.get('padding-right');
3484
+
3485
+ return width + pLeft + pRight;
3486
+ },
3487
+
3488
+ 'border-box-height': function(element) {
3489
+ if (!this._preComputing) this._begin();
3490
+ var height = element.offsetHeight;
3491
+ if (!this._preComputing) this._end();
3492
+ return height;
3493
+ },
3494
+
3495
+ 'border-box-width': function(element) {
3496
+ if (!this._preComputing) this._begin();
3497
+ var width = element.offsetWidth;
3498
+ if (!this._preComputing) this._end();
3499
+ return width;
3500
+ },
3501
+
3502
+ 'margin-box-height': function(element) {
3503
+ var bHeight = this.get('border-box-height'),
3504
+ mTop = this.get('margin-top'),
3505
+ mBottom = this.get('margin-bottom');
3506
+
3507
+ if (bHeight <= 0) return 0;
3508
+
3509
+ return bHeight + mTop + mBottom;
3510
+ },
3511
+
3512
+ 'margin-box-width': function(element) {
3513
+ var bWidth = this.get('border-box-width'),
3514
+ mLeft = this.get('margin-left'),
3515
+ mRight = this.get('margin-right');
3516
+
3517
+ if (bWidth <= 0) return 0;
3518
+
3519
+ return bWidth + mLeft + mRight;
3520
+ },
3521
+
3522
+ 'top': function(element) {
3523
+ var offset = element.positionedOffset();
3524
+ return offset.top;
3525
+ },
3526
+
3527
+ 'bottom': function(element) {
3528
+ var offset = element.positionedOffset(),
3529
+ parent = element.getOffsetParent(),
3530
+ pHeight = parent.measure('height');
3531
+
3532
+ var mHeight = this.get('border-box-height');
3533
+
3534
+ return pHeight - mHeight - offset.top;
3535
+ },
3536
+
3537
+ 'left': function(element) {
3538
+ var offset = element.positionedOffset();
3539
+ return offset.left;
3540
+ },
3541
+
3542
+ 'right': function(element) {
3543
+ var offset = element.positionedOffset(),
3544
+ parent = element.getOffsetParent(),
3545
+ pWidth = parent.measure('width');
3546
+
3547
+ var mWidth = this.get('border-box-width');
3548
+
3549
+ return pWidth - mWidth - offset.left;
3550
+ },
3551
+
3552
+ 'padding-top': function(element) {
3553
+ return getPixelValue(element, 'paddingTop');
3554
+ },
3555
+
3556
+ 'padding-bottom': function(element) {
3557
+ return getPixelValue(element, 'paddingBottom');
3558
+ },
3559
+
3560
+ 'padding-left': function(element) {
3561
+ return getPixelValue(element, 'paddingLeft');
3562
+ },
3563
+
3564
+ 'padding-right': function(element) {
3565
+ return getPixelValue(element, 'paddingRight');
3566
+ },
3567
+
3568
+ 'border-top': function(element) {
3569
+ return getPixelValue(element, 'borderTopWidth');
3570
+ },
3571
+
3572
+ 'border-bottom': function(element) {
3573
+ return getPixelValue(element, 'borderBottomWidth');
3574
+ },
3575
+
3576
+ 'border-left': function(element) {
3577
+ return getPixelValue(element, 'borderLeftWidth');
3578
+ },
3579
+
3580
+ 'border-right': function(element) {
3581
+ return getPixelValue(element, 'borderRightWidth');
3582
+ },
3583
+
3584
+ 'margin-top': function(element) {
3585
+ return getPixelValue(element, 'marginTop');
3586
+ },
3587
+
3588
+ 'margin-bottom': function(element) {
3589
+ return getPixelValue(element, 'marginBottom');
3590
+ },
3591
+
3592
+ 'margin-left': function(element) {
3593
+ return getPixelValue(element, 'marginLeft');
3594
+ },
3595
+
3596
+ 'margin-right': function(element) {
3597
+ return getPixelValue(element, 'marginRight');
3598
+ }
3599
+ }
3600
+ });
3601
+
3602
+ if ('getBoundingClientRect' in document.documentElement) {
3603
+ Object.extend(Element.Layout.COMPUTATIONS, {
3604
+ 'right': function(element) {
3605
+ var parent = hasLayout(element.getOffsetParent());
3606
+ var rect = element.getBoundingClientRect(),
3607
+ pRect = parent.getBoundingClientRect();
3608
+
3609
+ return (pRect.right - rect.right).round();
3610
+ },
3611
+
3612
+ 'bottom': function(element) {
3613
+ var parent = hasLayout(element.getOffsetParent());
3614
+ var rect = element.getBoundingClientRect(),
3615
+ pRect = parent.getBoundingClientRect();
3616
+
3617
+ return (pRect.bottom - rect.bottom).round();
3618
+ }
3619
+ });
3620
+ }
3621
+
3622
+ Element.Offset = Class.create({
3623
+ initialize: function(left, top) {
3624
+ this.left = left.round();
3625
+ this.top = top.round();
3626
+
3627
+ this[0] = this.left;
3628
+ this[1] = this.top;
3629
+ },
3630
+
3631
+ relativeTo: function(offset) {
3632
+ return new Element.Offset(
3633
+ this.left - offset.left,
3634
+ this.top - offset.top
3635
+ );
3636
+ },
3637
+
3638
+ inspect: function() {
3639
+ return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
3640
+ },
3641
+
3642
+ toString: function() {
3643
+ return "[#{left}, #{top}]".interpolate(this);
3644
+ },
3645
+
3646
+ toArray: function() {
3647
+ return [this.left, this.top];
3648
+ }
3649
+ });
3650
+
3651
+ function getLayout(element, preCompute) {
3652
+ return new Element.Layout(element, preCompute);
3653
+ }
3654
+
3655
+ function measure(element, property) {
3656
+ return $(element).getLayout().get(property);
3657
+ }
3658
+
3659
+ function getDimensions(element) {
3660
+ element = $(element);
3661
+ var display = Element.getStyle(element, 'display');
3662
+
3663
+ if (display && display !== 'none') {
3664
+ return { width: element.offsetWidth, height: element.offsetHeight };
3665
+ }
3666
+
3667
+ var style = element.style;
3668
+ var originalStyles = {
3669
+ visibility: style.visibility,
3670
+ position: style.position,
3671
+ display: style.display
3672
+ };
3673
+
3674
+ var newStyles = {
3675
+ visibility: 'hidden',
3676
+ display: 'block'
3677
+ };
3678
+
3679
+ if (originalStyles.position !== 'fixed')
3680
+ newStyles.position = 'absolute';
3681
+
3682
+ Element.setStyle(element, newStyles);
3683
+
3684
+ var dimensions = {
3685
+ width: element.offsetWidth,
3686
+ height: element.offsetHeight
3687
+ };
3688
+
3689
+ Element.setStyle(element, originalStyles);
3690
+
3691
+ return dimensions;
3692
+ }
3693
+
3694
+ function getOffsetParent(element) {
3695
+ element = $(element);
3696
+
3697
+ if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3698
+ return $(document.body);
3699
+
3700
+ var isInline = (Element.getStyle(element, 'display') === 'inline');
3701
+ if (!isInline && element.offsetParent) return $(element.offsetParent);
3702
+
3703
+ while ((element = element.parentNode) && element !== document.body) {
3704
+ if (Element.getStyle(element, 'position') !== 'static') {
3705
+ return isHtml(element) ? $(document.body) : $(element);
3706
+ }
3707
+ }
3708
+
3709
+ return $(document.body);
3710
+ }
3711
+
3712
+
3713
+ function cumulativeOffset(element) {
3714
+ element = $(element);
3715
+ var valueT = 0, valueL = 0;
3716
+ if (element.parentNode) {
3717
+ do {
3718
+ valueT += element.offsetTop || 0;
3719
+ valueL += element.offsetLeft || 0;
3720
+ element = element.offsetParent;
3721
+ } while (element);
3722
+ }
3723
+ return new Element.Offset(valueL, valueT);
3724
+ }
3725
+
3726
+ function positionedOffset(element) {
3727
+ element = $(element);
3728
+
3729
+ var layout = element.getLayout();
3730
+
3731
+ var valueT = 0, valueL = 0;
3732
+ do {
3733
+ valueT += element.offsetTop || 0;
3734
+ valueL += element.offsetLeft || 0;
3735
+ element = element.offsetParent;
3736
+ if (element) {
3737
+ if (isBody(element)) break;
3738
+ var p = Element.getStyle(element, 'position');
3739
+ if (p !== 'static') break;
3740
+ }
3741
+ } while (element);
3742
+
3743
+ valueL -= layout.get('margin-top');
3744
+ valueT -= layout.get('margin-left');
3745
+
3746
+ return new Element.Offset(valueL, valueT);
3747
+ }
3748
+
3749
+ function cumulativeScrollOffset(element) {
3750
+ var valueT = 0, valueL = 0;
3751
+ do {
3752
+ valueT += element.scrollTop || 0;
3753
+ valueL += element.scrollLeft || 0;
3754
+ element = element.parentNode;
3755
+ } while (element);
3756
+ return new Element.Offset(valueL, valueT);
3757
+ }
3758
+
3759
+ function viewportOffset(forElement) {
3760
+ element = $(element);
3761
+ var valueT = 0, valueL = 0, docBody = document.body;
3762
+
3763
+ var element = forElement;
3764
+ do {
3765
+ valueT += element.offsetTop || 0;
3766
+ valueL += element.offsetLeft || 0;
3767
+ if (element.offsetParent == docBody &&
3768
+ Element.getStyle(element, 'position') == 'absolute') break;
3769
+ } while (element = element.offsetParent);
3770
+
3771
+ element = forElement;
3772
+ do {
3773
+ if (element != docBody) {
3774
+ valueT -= element.scrollTop || 0;
3775
+ valueL -= element.scrollLeft || 0;
3776
+ }
3777
+ } while (element = element.parentNode);
3778
+ return new Element.Offset(valueL, valueT);
3779
+ }
3780
+
3781
+ function absolutize(element) {
3782
+ element = $(element);
3783
+
3784
+ if (Element.getStyle(element, 'position') === 'absolute') {
3785
+ return element;
3786
+ }
3787
+
3788
+ var offsetParent = getOffsetParent(element);
3789
+ var eOffset = element.viewportOffset(),
3790
+ pOffset = offsetParent.viewportOffset();
3791
+
3792
+ var offset = eOffset.relativeTo(pOffset);
3793
+ var layout = element.getLayout();
3794
+
3795
+ element.store('prototype_absolutize_original_styles', {
3796
+ left: element.getStyle('left'),
3797
+ top: element.getStyle('top'),
3798
+ width: element.getStyle('width'),
3799
+ height: element.getStyle('height')
3800
+ });
3801
+
3802
+ element.setStyle({
3803
+ position: 'absolute',
3804
+ top: offset.top + 'px',
3805
+ left: offset.left + 'px',
3806
+ width: layout.get('width') + 'px',
3807
+ height: layout.get('height') + 'px'
3808
+ });
3809
+
3810
+ return element;
3811
+ }
3812
+
3813
+ function relativize(element) {
3814
+ element = $(element);
3815
+ if (Element.getStyle(element, 'position') === 'relative') {
3816
+ return element;
3817
+ }
3818
+
3819
+ var originalStyles =
3820
+ element.retrieve('prototype_absolutize_original_styles');
3821
+
3822
+ if (originalStyles) element.setStyle(originalStyles);
3823
+ return element;
3824
+ }
3825
+
3826
+ if (Prototype.Browser.IE) {
3827
+ getOffsetParent = getOffsetParent.wrap(
3828
+ function(proceed, element) {
3829
+ element = $(element);
3830
+
3831
+ if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3832
+ return $(document.body);
3833
+
3834
+ var position = element.getStyle('position');
3835
+ if (position !== 'static') return proceed(element);
3836
+
3837
+ element.setStyle({ position: 'relative' });
3838
+ var value = proceed(element);
3839
+ element.setStyle({ position: position });
3840
+ return value;
3841
+ }
3842
+ );
3843
+
3844
+ positionedOffset = positionedOffset.wrap(function(proceed, element) {
3845
+ element = $(element);
3846
+ if (!element.parentNode) return new Element.Offset(0, 0);
3847
+ var position = element.getStyle('position');
3848
+ if (position !== 'static') return proceed(element);
3849
+
3850
+ var offsetParent = element.getOffsetParent();
3851
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
3852
+ hasLayout(offsetParent);
3853
+
3854
+ element.setStyle({ position: 'relative' });
3855
+ var value = proceed(element);
3856
+ element.setStyle({ position: position });
3857
+ return value;
3858
+ });
3859
+ } else if (Prototype.Browser.Webkit) {
3860
+ cumulativeOffset = function(element) {
3861
+ element = $(element);
3862
+ var valueT = 0, valueL = 0;
3863
+ do {
3864
+ valueT += element.offsetTop || 0;
3865
+ valueL += element.offsetLeft || 0;
3866
+ if (element.offsetParent == document.body)
3867
+ if (Element.getStyle(element, 'position') == 'absolute') break;
3868
+
3869
+ element = element.offsetParent;
3870
+ } while (element);
3871
+
3872
+ return new Element.Offset(valueL, valueT);
3873
+ };
3874
+ }
3875
+
3876
+
3877
+ Element.addMethods({
3878
+ getLayout: getLayout,
3879
+ measure: measure,
3880
+ getDimensions: getDimensions,
3881
+ getOffsetParent: getOffsetParent,
3882
+ cumulativeOffset: cumulativeOffset,
3883
+ positionedOffset: positionedOffset,
3884
+ cumulativeScrollOffset: cumulativeScrollOffset,
3885
+ viewportOffset: viewportOffset,
3886
+ absolutize: absolutize,
3887
+ relativize: relativize
3888
+ });
3889
+
3890
+ function isBody(element) {
3891
+ return element.nodeName.toUpperCase() === 'BODY';
3892
+ }
3893
+
3894
+ function isHtml(element) {
3895
+ return element.nodeName.toUpperCase() === 'HTML';
3896
+ }
3897
+
3898
+ function isDocument(element) {
3899
+ return element.nodeType === Node.DOCUMENT_NODE;
3900
+ }
3901
+
3902
+ function isDetached(element) {
3903
+ return element !== document.body &&
3904
+ !Element.descendantOf(element, document.body);
3905
+ }
3906
+
3907
+ if ('getBoundingClientRect' in document.documentElement) {
3908
+ Element.addMethods({
3909
+ viewportOffset: function(element) {
3910
+ element = $(element);
3911
+ if (isDetached(element)) return new Element.Offset(0, 0);
3912
+
3913
+ var rect = element.getBoundingClientRect(),
3914
+ docEl = document.documentElement;
3915
+ return new Element.Offset(rect.left - docEl.clientLeft,
3916
+ rect.top - docEl.clientTop);
3917
+ }
3918
+ });
3919
+ }
3920
+ })();
3921
+ window.$$ = function() {
3922
+ var expression = $A(arguments).join(', ');
3923
+ return Prototype.Selector.select(expression, document);
3924
+ };
3925
+
3926
+ Prototype.Selector = (function() {
3927
+
3928
+ function select() {
3929
+ throw new Error('Method "Prototype.Selector.select" must be defined.');
3930
+ }
3931
+
3932
+ function match() {
3933
+ throw new Error('Method "Prototype.Selector.match" must be defined.');
3934
+ }
3935
+
3936
+ function find(elements, expression, index) {
3937
+ index = index || 0;
3938
+ var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
3939
+
3940
+ for (i = 0; i < length; i++) {
3941
+ if (match(elements[i], expression) && index == matchIndex++) {
3942
+ return Element.extend(elements[i]);
3943
+ }
3944
+ }
3945
+ }
3946
+
3947
+ function extendElements(elements) {
3948
+ for (var i = 0, length = elements.length; i < length; i++) {
3949
+ Element.extend(elements[i]);
3950
+ }
3951
+ return elements;
3952
+ }
3953
+
3954
+
3955
+ var K = Prototype.K;
3956
+
3957
+ return {
3958
+ select: select,
3959
+ match: match,
3960
+ find: find,
3961
+ extendElements: (Element.extend === K) ? K : extendElements,
3962
+ extendElement: Element.extend
3963
+ };
3964
+ })();
3965
+ Prototype._original_property = window.Sizzle;
3966
+ /*!
3967
+ * Sizzle CSS Selector Engine - v1.0
3968
+ * Copyright 2009, The Dojo Foundation
3969
+ * Released under the MIT, BSD, and GPL Licenses.
3970
+ * More information: http://sizzlejs.com/
3971
+ */
3972
+ (function(){
3973
+
3974
+ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
3975
+ done = 0,
3976
+ toString = Object.prototype.toString,
3977
+ hasDuplicate = false,
3978
+ baseHasDuplicate = true;
3979
+
3980
+ [0, 0].sort(function(){
3981
+ baseHasDuplicate = false;
3982
+ return 0;
3983
+ });
3984
+
3985
+ var Sizzle = function(selector, context, results, seed) {
3986
+ results = results || [];
3987
+ var origContext = context = context || document;
3988
+
3989
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
3990
+ return [];
3991
+ }
3992
+
3993
+ if ( !selector || typeof selector !== "string" ) {
3994
+ return results;
3995
+ }
3996
+
3997
+ var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
3998
+ soFar = selector;
3999
+
4000
+ while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
4001
+ soFar = m[3];
4002
+
4003
+ parts.push( m[1] );
4004
+
4005
+ if ( m[2] ) {
4006
+ extra = m[3];
4007
+ break;
4008
+ }
4009
+ }
4010
+
4011
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
4012
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
4013
+ set = posProcess( parts[0] + parts[1], context );
4014
+ } else {
4015
+ set = Expr.relative[ parts[0] ] ?
4016
+ [ context ] :
4017
+ Sizzle( parts.shift(), context );
4018
+
4019
+ while ( parts.length ) {
4020
+ selector = parts.shift();
4021
+
4022
+ if ( Expr.relative[ selector ] )
4023
+ selector += parts.shift();
4024
+
4025
+ set = posProcess( selector, set );
4026
+ }
4027
+ }
4028
+ } else {
4029
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
4030
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
4031
+ var ret = Sizzle.find( parts.shift(), context, contextXML );
4032
+ context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
4033
+ }
4034
+
4035
+ if ( context ) {
4036
+ var ret = seed ?
4037
+ { expr: parts.pop(), set: makeArray(seed) } :
4038
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
4039
+ set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
4040
+
4041
+ if ( parts.length > 0 ) {
4042
+ checkSet = makeArray(set);
4043
+ } else {
4044
+ prune = false;
4045
+ }
4046
+
4047
+ while ( parts.length ) {
4048
+ var cur = parts.pop(), pop = cur;
4049
+
4050
+ if ( !Expr.relative[ cur ] ) {
4051
+ cur = "";
4052
+ } else {
4053
+ pop = parts.pop();
4054
+ }
4055
+
4056
+ if ( pop == null ) {
4057
+ pop = context;
4058
+ }
4059
+
4060
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
4061
+ }
4062
+ } else {
4063
+ checkSet = parts = [];
4064
+ }
4065
+ }
4066
+
4067
+ if ( !checkSet ) {
4068
+ checkSet = set;
4069
+ }
4070
+
4071
+ if ( !checkSet ) {
4072
+ throw "Syntax error, unrecognized expression: " + (cur || selector);
4073
+ }
4074
+
4075
+ if ( toString.call(checkSet) === "[object Array]" ) {
4076
+ if ( !prune ) {
4077
+ results.push.apply( results, checkSet );
4078
+ } else if ( context && context.nodeType === 1 ) {
4079
+ for ( var i = 0; checkSet[i] != null; i++ ) {
4080
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
4081
+ results.push( set[i] );
4082
+ }
4083
+ }
4084
+ } else {
4085
+ for ( var i = 0; checkSet[i] != null; i++ ) {
4086
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
4087
+ results.push( set[i] );
4088
+ }
4089
+ }
4090
+ }
4091
+ } else {
4092
+ makeArray( checkSet, results );
4093
+ }
4094
+
4095
+ if ( extra ) {
4096
+ Sizzle( extra, origContext, results, seed );
4097
+ Sizzle.uniqueSort( results );
4098
+ }
4099
+
4100
+ return results;
4101
+ };
4102
+
4103
+ Sizzle.uniqueSort = function(results){
4104
+ if ( sortOrder ) {
4105
+ hasDuplicate = baseHasDuplicate;
4106
+ results.sort(sortOrder);
4107
+
4108
+ if ( hasDuplicate ) {
4109
+ for ( var i = 1; i < results.length; i++ ) {
4110
+ if ( results[i] === results[i-1] ) {
4111
+ results.splice(i--, 1);
4112
+ }
4113
+ }
4114
+ }
4115
+ }
4116
+
4117
+ return results;
4118
+ };
4119
+
4120
+ Sizzle.matches = function(expr, set){
4121
+ return Sizzle(expr, null, null, set);
4122
+ };
4123
+
4124
+ Sizzle.find = function(expr, context, isXML){
4125
+ var set, match;
4126
+
4127
+ if ( !expr ) {
4128
+ return [];
4129
+ }
4130
+
4131
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
4132
+ var type = Expr.order[i], match;
4133
+
4134
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
4135
+ var left = match[1];
4136
+ match.splice(1,1);
4137
+
4138
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
4139
+ match[1] = (match[1] || "").replace(/\\/g, "");
4140
+ set = Expr.find[ type ]( match, context, isXML );
4141
+ if ( set != null ) {
4142
+ expr = expr.replace( Expr.match[ type ], "" );
4143
+ break;
4144
+ }
4145
+ }
4146
+ }
4147
+ }
4148
+
4149
+ if ( !set ) {
4150
+ set = context.getElementsByTagName("*");
4151
+ }
4152
+
4153
+ return {set: set, expr: expr};
4154
+ };
4155
+
4156
+ Sizzle.filter = function(expr, set, inplace, not){
4157
+ var old = expr, result = [], curLoop = set, match, anyFound,
4158
+ isXMLFilter = set && set[0] && isXML(set[0]);
4159
+
4160
+ while ( expr && set.length ) {
4161
+ for ( var type in Expr.filter ) {
4162
+ if ( (match = Expr.match[ type ].exec( expr )) != null ) {
4163
+ var filter = Expr.filter[ type ], found, item;
4164
+ anyFound = false;
4165
+
4166
+ if ( curLoop == result ) {
4167
+ result = [];
4168
+ }
4169
+
4170
+ if ( Expr.preFilter[ type ] ) {
4171
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
4172
+
4173
+ if ( !match ) {
4174
+ anyFound = found = true;
4175
+ } else if ( match === true ) {
4176
+ continue;
4177
+ }
4178
+ }
4179
+
4180
+ if ( match ) {
4181
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
4182
+ if ( item ) {
4183
+ found = filter( item, match, i, curLoop );
4184
+ var pass = not ^ !!found;
4185
+
4186
+ if ( inplace && found != null ) {
4187
+ if ( pass ) {
4188
+ anyFound = true;
4189
+ } else {
4190
+ curLoop[i] = false;
4191
+ }
4192
+ } else if ( pass ) {
4193
+ result.push( item );
4194
+ anyFound = true;
4195
+ }
4196
+ }
4197
+ }
4198
+ }
4199
+
4200
+ if ( found !== undefined ) {
4201
+ if ( !inplace ) {
4202
+ curLoop = result;
4203
+ }
4204
+
4205
+ expr = expr.replace( Expr.match[ type ], "" );
4206
+
4207
+ if ( !anyFound ) {
4208
+ return [];
4209
+ }
4210
+
4211
+ break;
4212
+ }
4213
+ }
4214
+ }
4215
+
4216
+ if ( expr == old ) {
4217
+ if ( anyFound == null ) {
4218
+ throw "Syntax error, unrecognized expression: " + expr;
4219
+ } else {
4220
+ break;
4221
+ }
4222
+ }
4223
+
4224
+ old = expr;
4225
+ }
4226
+
4227
+ return curLoop;
4228
+ };
4229
+
4230
+ var Expr = Sizzle.selectors = {
4231
+ order: [ "ID", "NAME", "TAG" ],
4232
+ match: {
4233
+ ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4234
+ CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4235
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
4236
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
4237
+ TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
4238
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
4239
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
4240
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
4241
+ },
4242
+ leftMatch: {},
4243
+ attrMap: {
4244
+ "class": "className",
4245
+ "for": "htmlFor"
4246
+ },
4247
+ attrHandle: {
4248
+ href: function(elem){
4249
+ return elem.getAttribute("href");
4250
+ }
4251
+ },
4252
+ relative: {
4253
+ "+": function(checkSet, part, isXML){
4254
+ var isPartStr = typeof part === "string",
4255
+ isTag = isPartStr && !/\W/.test(part),
4256
+ isPartStrNotTag = isPartStr && !isTag;
4257
+
4258
+ if ( isTag && !isXML ) {
4259
+ part = part.toUpperCase();
4260
+ }
4261
+
4262
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
4263
+ if ( (elem = checkSet[i]) ) {
4264
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
4265
+
4266
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
4267
+ elem || false :
4268
+ elem === part;
4269
+ }
4270
+ }
4271
+
4272
+ if ( isPartStrNotTag ) {
4273
+ Sizzle.filter( part, checkSet, true );
4274
+ }
4275
+ },
4276
+ ">": function(checkSet, part, isXML){
4277
+ var isPartStr = typeof part === "string";
4278
+
4279
+ if ( isPartStr && !/\W/.test(part) ) {
4280
+ part = isXML ? part : part.toUpperCase();
4281
+
4282
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4283
+ var elem = checkSet[i];
4284
+ if ( elem ) {
4285
+ var parent = elem.parentNode;
4286
+ checkSet[i] = parent.nodeName === part ? parent : false;
4287
+ }
4288
+ }
4289
+ } else {
4290
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4291
+ var elem = checkSet[i];
4292
+ if ( elem ) {
4293
+ checkSet[i] = isPartStr ?
4294
+ elem.parentNode :
4295
+ elem.parentNode === part;
4296
+ }
4297
+ }
4298
+
4299
+ if ( isPartStr ) {
4300
+ Sizzle.filter( part, checkSet, true );
4301
+ }
4302
+ }
4303
+ },
4304
+ "": function(checkSet, part, isXML){
4305
+ var doneName = done++, checkFn = dirCheck;
4306
+
4307
+ if ( !/\W/.test(part) ) {
4308
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
4309
+ checkFn = dirNodeCheck;
4310
+ }
4311
+
4312
+ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
4313
+ },
4314
+ "~": function(checkSet, part, isXML){
4315
+ var doneName = done++, checkFn = dirCheck;
4316
+
4317
+ if ( typeof part === "string" && !/\W/.test(part) ) {
4318
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
4319
+ checkFn = dirNodeCheck;
4320
+ }
4321
+
4322
+ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
4323
+ }
4324
+ },
4325
+ find: {
4326
+ ID: function(match, context, isXML){
4327
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
4328
+ var m = context.getElementById(match[1]);
4329
+ return m ? [m] : [];
4330
+ }
4331
+ },
4332
+ NAME: function(match, context, isXML){
4333
+ if ( typeof context.getElementsByName !== "undefined" ) {
4334
+ var ret = [], results = context.getElementsByName(match[1]);
4335
+
4336
+ for ( var i = 0, l = results.length; i < l; i++ ) {
4337
+ if ( results[i].getAttribute("name") === match[1] ) {
4338
+ ret.push( results[i] );
4339
+ }
4340
+ }
4341
+
4342
+ return ret.length === 0 ? null : ret;
4343
+ }
4344
+ },
4345
+ TAG: function(match, context){
4346
+ return context.getElementsByTagName(match[1]);
4347
+ }
4348
+ },
4349
+ preFilter: {
4350
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
4351
+ match = " " + match[1].replace(/\\/g, "") + " ";
4352
+
4353
+ if ( isXML ) {
4354
+ return match;
4355
+ }
4356
+
4357
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
4358
+ if ( elem ) {
4359
+ if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
4360
+ if ( !inplace )
4361
+ result.push( elem );
4362
+ } else if ( inplace ) {
4363
+ curLoop[i] = false;
4364
+ }
4365
+ }
4366
+ }
4367
+
4368
+ return false;
4369
+ },
4370
+ ID: function(match){
4371
+ return match[1].replace(/\\/g, "");
4372
+ },
4373
+ TAG: function(match, curLoop){
4374
+ for ( var i = 0; curLoop[i] === false; i++ ){}
4375
+ return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
4376
+ },
4377
+ CHILD: function(match){
4378
+ if ( match[1] == "nth" ) {
4379
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
4380
+ match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
4381
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
4382
+
4383
+ match[2] = (test[1] + (test[2] || 1)) - 0;
4384
+ match[3] = test[3] - 0;
4385
+ }
4386
+
4387
+ match[0] = done++;
4388
+
4389
+ return match;
4390
+ },
4391
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
4392
+ var name = match[1].replace(/\\/g, "");
4393
+
4394
+ if ( !isXML && Expr.attrMap[name] ) {
4395
+ match[1] = Expr.attrMap[name];
4396
+ }
4397
+
4398
+ if ( match[2] === "~=" ) {
4399
+ match[4] = " " + match[4] + " ";
4400
+ }
4401
+
4402
+ return match;
4403
+ },
4404
+ PSEUDO: function(match, curLoop, inplace, result, not){
4405
+ if ( match[1] === "not" ) {
4406
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
4407
+ match[3] = Sizzle(match[3], null, null, curLoop);
4408
+ } else {
4409
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
4410
+ if ( !inplace ) {
4411
+ result.push.apply( result, ret );
4412
+ }
4413
+ return false;
4414
+ }
4415
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
4416
+ return true;
4417
+ }
4418
+
4419
+ return match;
4420
+ },
4421
+ POS: function(match){
4422
+ match.unshift( true );
4423
+ return match;
4424
+ }
4425
+ },
4426
+ filters: {
4427
+ enabled: function(elem){
4428
+ return elem.disabled === false && elem.type !== "hidden";
4429
+ },
4430
+ disabled: function(elem){
4431
+ return elem.disabled === true;
4432
+ },
4433
+ checked: function(elem){
4434
+ return elem.checked === true;
4435
+ },
4436
+ selected: function(elem){
4437
+ elem.parentNode.selectedIndex;
4438
+ return elem.selected === true;
4439
+ },
4440
+ parent: function(elem){
4441
+ return !!elem.firstChild;
4442
+ },
4443
+ empty: function(elem){
4444
+ return !elem.firstChild;
4445
+ },
4446
+ has: function(elem, i, match){
4447
+ return !!Sizzle( match[3], elem ).length;
4448
+ },
4449
+ header: function(elem){
4450
+ return /h\d/i.test( elem.nodeName );
4451
+ },
4452
+ text: function(elem){
4453
+ return "text" === elem.type;
4454
+ },
4455
+ radio: function(elem){
4456
+ return "radio" === elem.type;
4457
+ },
4458
+ checkbox: function(elem){
4459
+ return "checkbox" === elem.type;
4460
+ },
4461
+ file: function(elem){
4462
+ return "file" === elem.type;
4463
+ },
4464
+ password: function(elem){
4465
+ return "password" === elem.type;
4466
+ },
4467
+ submit: function(elem){
4468
+ return "submit" === elem.type;
4469
+ },
4470
+ image: function(elem){
4471
+ return "image" === elem.type;
4472
+ },
4473
+ reset: function(elem){
4474
+ return "reset" === elem.type;
4475
+ },
4476
+ button: function(elem){
4477
+ return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
4478
+ },
4479
+ input: function(elem){
4480
+ return /input|select|textarea|button/i.test(elem.nodeName);
4481
+ }
4482
+ },
4483
+ setFilters: {
4484
+ first: function(elem, i){
4485
+ return i === 0;
4486
+ },
4487
+ last: function(elem, i, match, array){
4488
+ return i === array.length - 1;
4489
+ },
4490
+ even: function(elem, i){
4491
+ return i % 2 === 0;
4492
+ },
4493
+ odd: function(elem, i){
4494
+ return i % 2 === 1;
4495
+ },
4496
+ lt: function(elem, i, match){
4497
+ return i < match[3] - 0;
4498
+ },
4499
+ gt: function(elem, i, match){
4500
+ return i > match[3] - 0;
4501
+ },
4502
+ nth: function(elem, i, match){
4503
+ return match[3] - 0 == i;
4504
+ },
4505
+ eq: function(elem, i, match){
4506
+ return match[3] - 0 == i;
4507
+ }
4508
+ },
4509
+ filter: {
4510
+ PSEUDO: function(elem, match, i, array){
4511
+ var name = match[1], filter = Expr.filters[ name ];
4512
+
4513
+ if ( filter ) {
4514
+ return filter( elem, i, match, array );
4515
+ } else if ( name === "contains" ) {
4516
+ return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
4517
+ } else if ( name === "not" ) {
4518
+ var not = match[3];
4519
+
4520
+ for ( var i = 0, l = not.length; i < l; i++ ) {
4521
+ if ( not[i] === elem ) {
4522
+ return false;
4523
+ }
4524
+ }
4525
+
4526
+ return true;
4527
+ }
4528
+ },
4529
+ CHILD: function(elem, match){
4530
+ var type = match[1], node = elem;
4531
+ switch (type) {
4532
+ case 'only':
4533
+ case 'first':
4534
+ while ( (node = node.previousSibling) ) {
4535
+ if ( node.nodeType === 1 ) return false;
4536
+ }
4537
+ if ( type == 'first') return true;
4538
+ node = elem;
4539
+ case 'last':
4540
+ while ( (node = node.nextSibling) ) {
4541
+ if ( node.nodeType === 1 ) return false;
4542
+ }
4543
+ return true;
4544
+ case 'nth':
4545
+ var first = match[2], last = match[3];
4546
+
4547
+ if ( first == 1 && last == 0 ) {
4548
+ return true;
4549
+ }
4550
+
4551
+ var doneName = match[0],
4552
+ parent = elem.parentNode;
4553
+
4554
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
4555
+ var count = 0;
4556
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
4557
+ if ( node.nodeType === 1 ) {
4558
+ node.nodeIndex = ++count;
4559
+ }
4560
+ }
4561
+ parent.sizcache = doneName;
4562
+ }
4563
+
4564
+ var diff = elem.nodeIndex - last;
4565
+ if ( first == 0 ) {
4566
+ return diff == 0;
4567
+ } else {
4568
+ return ( diff % first == 0 && diff / first >= 0 );
4569
+ }
4570
+ }
4571
+ },
4572
+ ID: function(elem, match){
4573
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
4574
+ },
4575
+ TAG: function(elem, match){
4576
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
4577
+ },
4578
+ CLASS: function(elem, match){
4579
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
4580
+ .indexOf( match ) > -1;
4581
+ },
4582
+ ATTR: function(elem, match){
4583
+ var name = match[1],
4584
+ result = Expr.attrHandle[ name ] ?
4585
+ Expr.attrHandle[ name ]( elem ) :
4586
+ elem[ name ] != null ?
4587
+ elem[ name ] :
4588
+ elem.getAttribute( name ),
4589
+ value = result + "",
4590
+ type = match[2],
4591
+ check = match[4];
4592
+
4593
+ return result == null ?
4594
+ type === "!=" :
4595
+ type === "=" ?
4596
+ value === check :
4597
+ type === "*=" ?
4598
+ value.indexOf(check) >= 0 :
4599
+ type === "~=" ?
4600
+ (" " + value + " ").indexOf(check) >= 0 :
4601
+ !check ?
4602
+ value && result !== false :
4603
+ type === "!=" ?
4604
+ value != check :
4605
+ type === "^=" ?
4606
+ value.indexOf(check) === 0 :
4607
+ type === "$=" ?
4608
+ value.substr(value.length - check.length) === check :
4609
+ type === "|=" ?
4610
+ value === check || value.substr(0, check.length + 1) === check + "-" :
4611
+ false;
4612
+ },
4613
+ POS: function(elem, match, i, array){
4614
+ var name = match[2], filter = Expr.setFilters[ name ];
4615
+
4616
+ if ( filter ) {
4617
+ return filter( elem, i, match, array );
4618
+ }
4619
+ }
4620
+ }
4621
+ };
4622
+
4623
+ var origPOS = Expr.match.POS;
4624
+
4625
+ for ( var type in Expr.match ) {
4626
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
4627
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
4628
+ }
4629
+
4630
+ var makeArray = function(array, results) {
4631
+ array = Array.prototype.slice.call( array, 0 );
4632
+
4633
+ if ( results ) {
4634
+ results.push.apply( results, array );
4635
+ return results;
4636
+ }
4637
+
4638
+ return array;
4639
+ };
4640
+
4641
+ try {
4642
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 );
4643
+
4644
+ } catch(e){
4645
+ makeArray = function(array, results) {
4646
+ var ret = results || [];
4647
+
4648
+ if ( toString.call(array) === "[object Array]" ) {
4649
+ Array.prototype.push.apply( ret, array );
4650
+ } else {
4651
+ if ( typeof array.length === "number" ) {
4652
+ for ( var i = 0, l = array.length; i < l; i++ ) {
4653
+ ret.push( array[i] );
4654
+ }
4655
+ } else {
4656
+ for ( var i = 0; array[i]; i++ ) {
4657
+ ret.push( array[i] );
4658
+ }
4659
+ }
4660
+ }
4661
+
4662
+ return ret;
4663
+ };
4664
+ }
4665
+
4666
+ var sortOrder;
4667
+
4668
+ if ( document.documentElement.compareDocumentPosition ) {
4669
+ sortOrder = function( a, b ) {
4670
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
4671
+ if ( a == b ) {
4672
+ hasDuplicate = true;
4673
+ }
4674
+ return 0;
4675
+ }
4676
+
4677
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
4678
+ if ( ret === 0 ) {
4679
+ hasDuplicate = true;
4680
+ }
4681
+ return ret;
4682
+ };
4683
+ } else if ( "sourceIndex" in document.documentElement ) {
4684
+ sortOrder = function( a, b ) {
4685
+ if ( !a.sourceIndex || !b.sourceIndex ) {
4686
+ if ( a == b ) {
4687
+ hasDuplicate = true;
4688
+ }
4689
+ return 0;
4690
+ }
4691
+
4692
+ var ret = a.sourceIndex - b.sourceIndex;
4693
+ if ( ret === 0 ) {
4694
+ hasDuplicate = true;
4695
+ }
4696
+ return ret;
4697
+ };
4698
+ } else if ( document.createRange ) {
4699
+ sortOrder = function( a, b ) {
4700
+ if ( !a.ownerDocument || !b.ownerDocument ) {
4701
+ if ( a == b ) {
4702
+ hasDuplicate = true;
4703
+ }
4704
+ return 0;
4705
+ }
4706
+
4707
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
4708
+ aRange.setStart(a, 0);
4709
+ aRange.setEnd(a, 0);
4710
+ bRange.setStart(b, 0);
4711
+ bRange.setEnd(b, 0);
4712
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
4713
+ if ( ret === 0 ) {
4714
+ hasDuplicate = true;
4715
+ }
4716
+ return ret;
4717
+ };
4718
+ }
4719
+
4720
+ (function(){
4721
+ var form = document.createElement("div"),
4722
+ id = "script" + (new Date).getTime();
4723
+ form.innerHTML = "<a name='" + id + "'/>";
4724
+
4725
+ var root = document.documentElement;
4726
+ root.insertBefore( form, root.firstChild );
4727
+
4728
+ if ( !!document.getElementById( id ) ) {
4729
+ Expr.find.ID = function(match, context, isXML){
4730
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
4731
+ var m = context.getElementById(match[1]);
4732
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
4733
+ }
4734
+ };
4735
+
4736
+ Expr.filter.ID = function(elem, match){
4737
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
4738
+ return elem.nodeType === 1 && node && node.nodeValue === match;
4739
+ };
4740
+ }
4741
+
4742
+ root.removeChild( form );
4743
+ root = form = null; // release memory in IE
4744
+ })();
4745
+
4746
+ (function(){
4747
+
4748
+ var div = document.createElement("div");
4749
+ div.appendChild( document.createComment("") );
4750
+
4751
+ if ( div.getElementsByTagName("*").length > 0 ) {
4752
+ Expr.find.TAG = function(match, context){
4753
+ var results = context.getElementsByTagName(match[1]);
4754
+
4755
+ if ( match[1] === "*" ) {
4756
+ var tmp = [];
4757
+
4758
+ for ( var i = 0; results[i]; i++ ) {
4759
+ if ( results[i].nodeType === 1 ) {
4760
+ tmp.push( results[i] );
4761
+ }
4762
+ }
4763
+
4764
+ results = tmp;
4765
+ }
4766
+
4767
+ return results;
4768
+ };
4769
+ }
4770
+
4771
+ div.innerHTML = "<a href='#'></a>";
4772
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
4773
+ div.firstChild.getAttribute("href") !== "#" ) {
4774
+ Expr.attrHandle.href = function(elem){
4775
+ return elem.getAttribute("href", 2);
4776
+ };
4777
+ }
4778
+
4779
+ div = null; // release memory in IE
4780
+ })();
4781
+
4782
+ if ( document.querySelectorAll ) (function(){
4783
+ var oldSizzle = Sizzle, div = document.createElement("div");
4784
+ div.innerHTML = "<p class='TEST'></p>";
4785
+
4786
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
4787
+ return;
4788
+ }
4789
+
4790
+ Sizzle = function(query, context, extra, seed){
4791
+ context = context || document;
4792
+
4793
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
4794
+ try {
4795
+ return makeArray( context.querySelectorAll(query), extra );
4796
+ } catch(e){}
4797
+ }
4798
+
4799
+ return oldSizzle(query, context, extra, seed);
4800
+ };
4801
+
4802
+ for ( var prop in oldSizzle ) {
4803
+ Sizzle[ prop ] = oldSizzle[ prop ];
4804
+ }
4805
+
4806
+ div = null; // release memory in IE
4807
+ })();
4808
+
4809
+ if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
4810
+ var div = document.createElement("div");
4811
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
4812
+
4813
+ if ( div.getElementsByClassName("e").length === 0 )
4814
+ return;
4815
+
4816
+ div.lastChild.className = "e";
4817
+
4818
+ if ( div.getElementsByClassName("e").length === 1 )
4819
+ return;
4820
+
4821
+ Expr.order.splice(1, 0, "CLASS");
4822
+ Expr.find.CLASS = function(match, context, isXML) {
4823
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
4824
+ return context.getElementsByClassName(match[1]);
4825
+ }
4826
+ };
4827
+
4828
+ div = null; // release memory in IE
4829
+ })();
4830
+
4831
+ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4832
+ var sibDir = dir == "previousSibling" && !isXML;
4833
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4834
+ var elem = checkSet[i];
4835
+ if ( elem ) {
4836
+ if ( sibDir && elem.nodeType === 1 ){
4837
+ elem.sizcache = doneName;
4838
+ elem.sizset = i;
4839
+ }
4840
+ elem = elem[dir];
4841
+ var match = false;
4842
+
4843
+ while ( elem ) {
4844
+ if ( elem.sizcache === doneName ) {
4845
+ match = checkSet[elem.sizset];
4846
+ break;
4847
+ }
4848
+
4849
+ if ( elem.nodeType === 1 && !isXML ){
4850
+ elem.sizcache = doneName;
4851
+ elem.sizset = i;
4852
+ }
4853
+
4854
+ if ( elem.nodeName === cur ) {
4855
+ match = elem;
4856
+ break;
4857
+ }
4858
+
4859
+ elem = elem[dir];
4860
+ }
4861
+
4862
+ checkSet[i] = match;
4863
+ }
4864
+ }
4865
+ }
4866
+
4867
+ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4868
+ var sibDir = dir == "previousSibling" && !isXML;
4869
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4870
+ var elem = checkSet[i];
4871
+ if ( elem ) {
4872
+ if ( sibDir && elem.nodeType === 1 ) {
4873
+ elem.sizcache = doneName;
4874
+ elem.sizset = i;
4875
+ }
4876
+ elem = elem[dir];
4877
+ var match = false;
4878
+
4879
+ while ( elem ) {
4880
+ if ( elem.sizcache === doneName ) {
4881
+ match = checkSet[elem.sizset];
4882
+ break;
4883
+ }
4884
+
4885
+ if ( elem.nodeType === 1 ) {
4886
+ if ( !isXML ) {
4887
+ elem.sizcache = doneName;
4888
+ elem.sizset = i;
4889
+ }
4890
+ if ( typeof cur !== "string" ) {
4891
+ if ( elem === cur ) {
4892
+ match = true;
4893
+ break;
4894
+ }
4895
+
4896
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
4897
+ match = elem;
4898
+ break;
4899
+ }
4900
+ }
4901
+
4902
+ elem = elem[dir];
4903
+ }
4904
+
4905
+ checkSet[i] = match;
4906
+ }
4907
+ }
4908
+ }
4909
+
4910
+ var contains = document.compareDocumentPosition ? function(a, b){
4911
+ return a.compareDocumentPosition(b) & 16;
4912
+ } : function(a, b){
4913
+ return a !== b && (a.contains ? a.contains(b) : true);
4914
+ };
4915
+
4916
+ var isXML = function(elem){
4917
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
4918
+ !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
4919
+ };
4920
+
4921
+ var posProcess = function(selector, context){
4922
+ var tmpSet = [], later = "", match,
4923
+ root = context.nodeType ? [context] : context;
4924
+
4925
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
4926
+ later += match[0];
4927
+ selector = selector.replace( Expr.match.PSEUDO, "" );
4928
+ }
4929
+
4930
+ selector = Expr.relative[selector] ? selector + "*" : selector;
4931
+
4932
+ for ( var i = 0, l = root.length; i < l; i++ ) {
4933
+ Sizzle( selector, root[i], tmpSet );
4934
+ }
4935
+
4936
+ return Sizzle.filter( later, tmpSet );
4937
+ };
4938
+
4939
+
4940
+ window.Sizzle = Sizzle;
4941
+
4942
+ })();
4943
+
4944
+ ;(function(engine) {
4945
+ var extendElements = Prototype.Selector.extendElements;
4946
+
4947
+ function select(selector, scope) {
4948
+ return extendElements(engine(selector, scope || document));
4949
+ }
4950
+
4951
+ function match(element, selector) {
4952
+ return engine.matches(selector, [element]).length == 1;
4953
+ }
4954
+
4955
+ Prototype.Selector.engine = engine;
4956
+ Prototype.Selector.select = select;
4957
+ Prototype.Selector.match = match;
4958
+ })(Sizzle);
4959
+
4960
+ window.Sizzle = Prototype._original_property;
4961
+ delete Prototype._original_property;
4962
+
4963
+ var Form = {
4964
+ reset: function(form) {
4965
+ form = $(form);
4966
+ form.reset();
4967
+ return form;
4968
+ },
4969
+
4970
+ serializeElements: function(elements, options) {
4971
+ if (typeof options != 'object') options = { hash: !!options };
4972
+ else if (Object.isUndefined(options.hash)) options.hash = true;
4973
+ var key, value, submitted = false, submit = options.submit, accumulator, initial;
4974
+
4975
+ if (options.hash) {
4976
+ initial = {};
4977
+ accumulator = function(result, key, value) {
4978
+ if (key in result) {
4979
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
4980
+ result[key].push(value);
4981
+ } else result[key] = value;
4982
+ return result;
4983
+ };
4984
+ } else {
4985
+ initial = '';
4986
+ accumulator = function(result, key, value) {
4987
+ return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value);
4988
+ }
4989
+ }
4990
+
4991
+ return elements.inject(initial, function(result, element) {
4992
+ if (!element.disabled && element.name) {
4993
+ key = element.name; value = $(element).getValue();
4994
+ if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
4995
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
4996
+ result = accumulator(result, key, value);
4997
+ }
4998
+ }
4999
+ return result;
5000
+ });
5001
+ }
5002
+ };
5003
+
5004
+ Form.Methods = {
5005
+ serialize: function(form, options) {
5006
+ return Form.serializeElements(Form.getElements(form), options);
5007
+ },
5008
+
5009
+ getElements: function(form) {
5010
+ var elements = $(form).getElementsByTagName('*'),
5011
+ element,
5012
+ arr = [ ],
5013
+ serializers = Form.Element.Serializers;
5014
+ for (var i = 0; element = elements[i]; i++) {
5015
+ arr.push(element);
5016
+ }
5017
+ return arr.inject([], function(elements, child) {
5018
+ if (serializers[child.tagName.toLowerCase()])
5019
+ elements.push(Element.extend(child));
5020
+ return elements;
5021
+ })
5022
+ },
5023
+
5024
+ getInputs: function(form, typeName, name) {
5025
+ form = $(form);
5026
+ var inputs = form.getElementsByTagName('input');
5027
+
5028
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
5029
+
5030
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
5031
+ var input = inputs[i];
5032
+ if ((typeName && input.type != typeName) || (name && input.name != name))
5033
+ continue;
5034
+ matchingInputs.push(Element.extend(input));
5035
+ }
5036
+
5037
+ return matchingInputs;
5038
+ },
5039
+
5040
+ disable: function(form) {
5041
+ form = $(form);
5042
+ Form.getElements(form).invoke('disable');
5043
+ return form;
5044
+ },
5045
+
5046
+ enable: function(form) {
5047
+ form = $(form);
5048
+ Form.getElements(form).invoke('enable');
5049
+ return form;
5050
+ },
5051
+
5052
+ findFirstElement: function(form) {
5053
+ var elements = $(form).getElements().findAll(function(element) {
5054
+ return 'hidden' != element.type && !element.disabled;
5055
+ });
5056
+ var firstByIndex = elements.findAll(function(element) {
5057
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
5058
+ }).sortBy(function(element) { return element.tabIndex }).first();
5059
+
5060
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
5061
+ return /^(?:input|select|textarea)$/i.test(element.tagName);
5062
+ });
5063
+ },
5064
+
5065
+ focusFirstElement: function(form) {
5066
+ form = $(form);
5067
+ var element = form.findFirstElement();
5068
+ if (element) element.activate();
5069
+ return form;
5070
+ },
5071
+
5072
+ request: function(form, options) {
5073
+ form = $(form), options = Object.clone(options || { });
5074
+
5075
+ var params = options.parameters, action = form.readAttribute('action') || '';
5076
+ if (action.blank()) action = window.location.href;
5077
+ options.parameters = form.serialize(true);
5078
+
5079
+ if (params) {
5080
+ if (Object.isString(params)) params = params.toQueryParams();
5081
+ Object.extend(options.parameters, params);
5082
+ }
5083
+
5084
+ if (form.hasAttribute('method') && !options.method)
5085
+ options.method = form.method;
5086
+
5087
+ return new Ajax.Request(action, options);
5088
+ }
5089
+ };
5090
+
5091
+ /*--------------------------------------------------------------------------*/
5092
+
5093
+
5094
+ Form.Element = {
5095
+ focus: function(element) {
5096
+ $(element).focus();
5097
+ return element;
5098
+ },
5099
+
5100
+ select: function(element) {
5101
+ $(element).select();
5102
+ return element;
5103
+ }
5104
+ };
5105
+
5106
+ Form.Element.Methods = {
5107
+
5108
+ serialize: function(element) {
5109
+ element = $(element);
5110
+ if (!element.disabled && element.name) {
5111
+ var value = element.getValue();
5112
+ if (value != undefined) {
5113
+ var pair = { };
5114
+ pair[element.name] = value;
5115
+ return Object.toQueryString(pair);
5116
+ }
5117
+ }
5118
+ return '';
5119
+ },
5120
+
5121
+ getValue: function(element) {
5122
+ element = $(element);
5123
+ var method = element.tagName.toLowerCase();
5124
+ return Form.Element.Serializers[method](element);
5125
+ },
5126
+
5127
+ setValue: function(element, value) {
5128
+ element = $(element);
5129
+ var method = element.tagName.toLowerCase();
5130
+ Form.Element.Serializers[method](element, value);
5131
+ return element;
5132
+ },
5133
+
5134
+ clear: function(element) {
5135
+ $(element).value = '';
5136
+ return element;
5137
+ },
5138
+
5139
+ present: function(element) {
5140
+ return $(element).value != '';
5141
+ },
5142
+
5143
+ activate: function(element) {
5144
+ element = $(element);
5145
+ try {
5146
+ element.focus();
5147
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
5148
+ !(/^(?:button|reset|submit)$/i.test(element.type))))
5149
+ element.select();
5150
+ } catch (e) { }
5151
+ return element;
5152
+ },
5153
+
5154
+ disable: function(element) {
5155
+ element = $(element);
5156
+ element.disabled = true;
5157
+ return element;
5158
+ },
5159
+
5160
+ enable: function(element) {
5161
+ element = $(element);
5162
+ element.disabled = false;
5163
+ return element;
5164
+ }
5165
+ };
5166
+
5167
+ /*--------------------------------------------------------------------------*/
5168
+
5169
+ var Field = Form.Element;
5170
+
5171
+ var $F = Form.Element.Methods.getValue;
5172
+
5173
+ /*--------------------------------------------------------------------------*/
5174
+
5175
+ Form.Element.Serializers = (function() {
5176
+ function input(element, value) {
5177
+ switch (element.type.toLowerCase()) {
5178
+ case 'checkbox':
5179
+ case 'radio':
5180
+ return inputSelector(element, value);
5181
+ default:
5182
+ return valueSelector(element, value);
5183
+ }
5184
+ }
5185
+
5186
+ function inputSelector(element, value) {
5187
+ if (Object.isUndefined(value))
5188
+ return element.checked ? element.value : null;
5189
+ else element.checked = !!value;
5190
+ }
5191
+
5192
+ function valueSelector(element, value) {
5193
+ if (Object.isUndefined(value)) return element.value;
5194
+ else element.value = value;
5195
+ }
5196
+
5197
+ function select(element, value) {
5198
+ if (Object.isUndefined(value))
5199
+ return (element.type === 'select-one' ? selectOne : selectMany)(element);
5200
+
5201
+ var opt, currentValue, single = !Object.isArray(value);
5202
+ for (var i = 0, length = element.length; i < length; i++) {
5203
+ opt = element.options[i];
5204
+ currentValue = this.optionValue(opt);
5205
+ if (single) {
5206
+ if (currentValue == value) {
5207
+ opt.selected = true;
5208
+ return;
5209
+ }
5210
+ }
5211
+ else opt.selected = value.include(currentValue);
5212
+ }
5213
+ }
5214
+
5215
+ function selectOne(element) {
5216
+ var index = element.selectedIndex;
5217
+ return index >= 0 ? optionValue(element.options[index]) : null;
5218
+ }
5219
+
5220
+ function selectMany(element) {
5221
+ var values, length = element.length;
5222
+ if (!length) return null;
5223
+
5224
+ for (var i = 0, values = []; i < length; i++) {
5225
+ var opt = element.options[i];
5226
+ if (opt.selected) values.push(optionValue(opt));
5227
+ }
5228
+ return values;
5229
+ }
5230
+
5231
+ function optionValue(opt) {
5232
+ return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
5233
+ }
5234
+
5235
+ return {
5236
+ input: input,
5237
+ inputSelector: inputSelector,
5238
+ textarea: valueSelector,
5239
+ select: select,
5240
+ selectOne: selectOne,
5241
+ selectMany: selectMany,
5242
+ optionValue: optionValue,
5243
+ button: valueSelector
5244
+ };
5245
+ })();
5246
+
5247
+ /*--------------------------------------------------------------------------*/
5248
+
5249
+
5250
+ Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
5251
+ initialize: function($super, element, frequency, callback) {
5252
+ $super(callback, frequency);
5253
+ this.element = $(element);
5254
+ this.lastValue = this.getValue();
5255
+ },
5256
+
5257
+ execute: function() {
5258
+ var value = this.getValue();
5259
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
5260
+ this.lastValue != value : String(this.lastValue) != String(value)) {
5261
+ this.callback(this.element, value);
5262
+ this.lastValue = value;
5263
+ }
5264
+ }
5265
+ });
5266
+
5267
+ Form.Element.Observer = Class.create(Abstract.TimedObserver, {
5268
+ getValue: function() {
5269
+ return Form.Element.getValue(this.element);
5270
+ }
5271
+ });
5272
+
5273
+ Form.Observer = Class.create(Abstract.TimedObserver, {
5274
+ getValue: function() {
5275
+ return Form.serialize(this.element);
5276
+ }
5277
+ });
5278
+
5279
+ /*--------------------------------------------------------------------------*/
5280
+
5281
+ Abstract.EventObserver = Class.create({
5282
+ initialize: function(element, callback) {
5283
+ this.element = $(element);
5284
+ this.callback = callback;
5285
+
5286
+ this.lastValue = this.getValue();
5287
+ if (this.element.tagName.toLowerCase() == 'form')
5288
+ this.registerFormCallbacks();
5289
+ else
5290
+ this.registerCallback(this.element);
5291
+ },
5292
+
5293
+ onElementEvent: function() {
5294
+ var value = this.getValue();
5295
+ if (this.lastValue != value) {
5296
+ this.callback(this.element, value);
5297
+ this.lastValue = value;
5298
+ }
5299
+ },
5300
+
5301
+ registerFormCallbacks: function() {
5302
+ Form.getElements(this.element).each(this.registerCallback, this);
5303
+ },
5304
+
5305
+ registerCallback: function(element) {
5306
+ if (element.type) {
5307
+ switch (element.type.toLowerCase()) {
5308
+ case 'checkbox':
5309
+ case 'radio':
5310
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
5311
+ break;
5312
+ default:
5313
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
5314
+ break;
5315
+ }
5316
+ }
5317
+ }
5318
+ });
5319
+
5320
+ Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
5321
+ getValue: function() {
5322
+ return Form.Element.getValue(this.element);
5323
+ }
5324
+ });
5325
+
5326
+ Form.EventObserver = Class.create(Abstract.EventObserver, {
5327
+ getValue: function() {
5328
+ return Form.serialize(this.element);
5329
+ }
5330
+ });
5331
+ (function() {
5332
+
5333
+ var Event = {
5334
+ KEY_BACKSPACE: 8,
5335
+ KEY_TAB: 9,
5336
+ KEY_RETURN: 13,
5337
+ KEY_ESC: 27,
5338
+ KEY_LEFT: 37,
5339
+ KEY_UP: 38,
5340
+ KEY_RIGHT: 39,
5341
+ KEY_DOWN: 40,
5342
+ KEY_DELETE: 46,
5343
+ KEY_HOME: 36,
5344
+ KEY_END: 35,
5345
+ KEY_PAGEUP: 33,
5346
+ KEY_PAGEDOWN: 34,
5347
+ KEY_INSERT: 45,
5348
+
5349
+ cache: {}
5350
+ };
5351
+
5352
+ var docEl = document.documentElement;
5353
+ var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
5354
+ && 'onmouseleave' in docEl;
5355
+
5356
+
5357
+
5358
+ var isIELegacyEvent = function(event) { return false; };
5359
+
5360
+ if (window.attachEvent) {
5361
+ if (window.addEventListener) {
5362
+ isIELegacyEvent = function(event) {
5363
+ return !(event instanceof window.Event);
5364
+ };
5365
+ } else {
5366
+ isIELegacyEvent = function(event) { return true; };
5367
+ }
5368
+ }
5369
+
5370
+ var _isButton;
5371
+
5372
+ function _isButtonForDOMEvents(event, code) {
5373
+ return event.which ? (event.which === code + 1) : (event.button === code);
5374
+ }
5375
+
5376
+ var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
5377
+ function _isButtonForLegacyEvents(event, code) {
5378
+ return event.button === legacyButtonMap[code];
5379
+ }
5380
+
5381
+ function _isButtonForWebKit(event, code) {
5382
+ switch (code) {
5383
+ case 0: return event.which == 1 && !event.metaKey;
5384
+ case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
5385
+ case 2: return event.which == 3;
5386
+ default: return false;
5387
+ }
5388
+ }
5389
+
5390
+ if (window.attachEvent) {
5391
+ if (!window.addEventListener) {
5392
+ _isButton = _isButtonForLegacyEvents;
5393
+ } else {
5394
+ _isButton = function(event, code) {
5395
+ return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
5396
+ _isButtonForDOMEvents(event, code);
5397
+ }
5398
+ }
5399
+ } else if (Prototype.Browser.WebKit) {
5400
+ _isButton = _isButtonForWebKit;
5401
+ } else {
5402
+ _isButton = _isButtonForDOMEvents;
5403
+ }
5404
+
5405
+ function isLeftClick(event) { return _isButton(event, 0) }
5406
+
5407
+ function isMiddleClick(event) { return _isButton(event, 1) }
5408
+
5409
+ function isRightClick(event) { return _isButton(event, 2) }
5410
+
5411
+ function element(event) {
5412
+ event = Event.extend(event);
5413
+
5414
+ var node = event.target, type = event.type,
5415
+ currentTarget = event.currentTarget;
5416
+
5417
+ if (currentTarget && currentTarget.tagName) {
5418
+ if (type === 'load' || type === 'error' ||
5419
+ (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
5420
+ && currentTarget.type === 'radio'))
5421
+ node = currentTarget;
5422
+ }
5423
+
5424
+ if (node.nodeType == Node.TEXT_NODE)
5425
+ node = node.parentNode;
5426
+
5427
+ return Element.extend(node);
5428
+ }
5429
+
5430
+ function findElement(event, expression) {
5431
+ var element = Event.element(event);
5432
+
5433
+ if (!expression) return element;
5434
+ while (element) {
5435
+ if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
5436
+ return Element.extend(element);
5437
+ }
5438
+ element = element.parentNode;
5439
+ }
5440
+ }
5441
+
5442
+ function pointer(event) {
5443
+ return { x: pointerX(event), y: pointerY(event) };
5444
+ }
5445
+
5446
+ function pointerX(event) {
5447
+ var docElement = document.documentElement,
5448
+ body = document.body || { scrollLeft: 0 };
5449
+
5450
+ return event.pageX || (event.clientX +
5451
+ (docElement.scrollLeft || body.scrollLeft) -
5452
+ (docElement.clientLeft || 0));
5453
+ }
5454
+
5455
+ function pointerY(event) {
5456
+ var docElement = document.documentElement,
5457
+ body = document.body || { scrollTop: 0 };
5458
+
5459
+ return event.pageY || (event.clientY +
5460
+ (docElement.scrollTop || body.scrollTop) -
5461
+ (docElement.clientTop || 0));
5462
+ }
5463
+
5464
+
5465
+ function stop(event) {
5466
+ Event.extend(event);
5467
+ event.preventDefault();
5468
+ event.stopPropagation();
5469
+
5470
+ event.stopped = true;
5471
+ }
5472
+
5473
+
5474
+ Event.Methods = {
5475
+ isLeftClick: isLeftClick,
5476
+ isMiddleClick: isMiddleClick,
5477
+ isRightClick: isRightClick,
5478
+
5479
+ element: element,
5480
+ findElement: findElement,
5481
+
5482
+ pointer: pointer,
5483
+ pointerX: pointerX,
5484
+ pointerY: pointerY,
5485
+
5486
+ stop: stop
5487
+ };
5488
+
5489
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
5490
+ m[name] = Event.Methods[name].methodize();
5491
+ return m;
5492
+ });
5493
+
5494
+ if (window.attachEvent) {
5495
+ function _relatedTarget(event) {
5496
+ var element;
5497
+ switch (event.type) {
5498
+ case 'mouseover':
5499
+ case 'mouseenter':
5500
+ element = event.fromElement;
5501
+ break;
5502
+ case 'mouseout':
5503
+ case 'mouseleave':
5504
+ element = event.toElement;
5505
+ break;
5506
+ default:
5507
+ return null;
5508
+ }
5509
+ return Element.extend(element);
5510
+ }
5511
+
5512
+ var additionalMethods = {
5513
+ stopPropagation: function() { this.cancelBubble = true },
5514
+ preventDefault: function() { this.returnValue = false },
5515
+ inspect: function() { return '[object Event]' }
5516
+ };
5517
+
5518
+ Event.extend = function(event, element) {
5519
+ if (!event) return false;
5520
+
5521
+ if (!isIELegacyEvent(event)) return event;
5522
+
5523
+ if (event._extendedByPrototype) return event;
5524
+ event._extendedByPrototype = Prototype.emptyFunction;
5525
+
5526
+ var pointer = Event.pointer(event);
5527
+
5528
+ Object.extend(event, {
5529
+ target: event.srcElement || element,
5530
+ relatedTarget: _relatedTarget(event),
5531
+ pageX: pointer.x,
5532
+ pageY: pointer.y
5533
+ });
5534
+
5535
+ Object.extend(event, methods);
5536
+ Object.extend(event, additionalMethods);
5537
+
5538
+ return event;
5539
+ };
5540
+ } else {
5541
+ Event.extend = Prototype.K;
5542
+ }
5543
+
5544
+ if (window.addEventListener) {
5545
+ Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
5546
+ Object.extend(Event.prototype, methods);
5547
+ }
5548
+
5549
+ function _createResponder(element, eventName, handler) {
5550
+ var registry = Element.retrieve(element, 'prototype_event_registry');
5551
+
5552
+ if (Object.isUndefined(registry)) {
5553
+ CACHE.push(element);
5554
+ registry = Element.retrieve(element, 'prototype_event_registry', $H());
5555
+ }
5556
+
5557
+ var respondersForEvent = registry.get(eventName);
5558
+ if (Object.isUndefined(respondersForEvent)) {
5559
+ respondersForEvent = [];
5560
+ registry.set(eventName, respondersForEvent);
5561
+ }
5562
+
5563
+ if (respondersForEvent.pluck('handler').include(handler)) return false;
5564
+
5565
+ var responder;
5566
+ if (eventName.include(":")) {
5567
+ responder = function(event) {
5568
+ if (Object.isUndefined(event.eventName))
5569
+ return false;
5570
+
5571
+ if (event.eventName !== eventName)
5572
+ return false;
5573
+
5574
+ Event.extend(event, element);
5575
+ handler.call(element, event);
5576
+ };
5577
+ } else {
5578
+ if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
5579
+ (eventName === "mouseenter" || eventName === "mouseleave")) {
5580
+ if (eventName === "mouseenter" || eventName === "mouseleave") {
5581
+ responder = function(event) {
5582
+ Event.extend(event, element);
5583
+
5584
+ var parent = event.relatedTarget;
5585
+ while (parent && parent !== element) {
5586
+ try { parent = parent.parentNode; }
5587
+ catch(e) { parent = element; }
5588
+ }
5589
+
5590
+ if (parent === element) return;
5591
+
5592
+ handler.call(element, event);
5593
+ };
5594
+ }
5595
+ } else {
5596
+ responder = function(event) {
5597
+ Event.extend(event, element);
5598
+ handler.call(element, event);
5599
+ };
5600
+ }
5601
+ }
5602
+
5603
+ responder.handler = handler;
5604
+ respondersForEvent.push(responder);
5605
+ return responder;
5606
+ }
5607
+
5608
+ function _destroyCache() {
5609
+ for (var i = 0, length = CACHE.length; i < length; i++) {
5610
+ Event.stopObserving(CACHE[i]);
5611
+ CACHE[i] = null;
5612
+ }
5613
+ }
5614
+
5615
+ var CACHE = [];
5616
+
5617
+ if (Prototype.Browser.IE)
5618
+ window.attachEvent('onunload', _destroyCache);
5619
+
5620
+ if (Prototype.Browser.WebKit)
5621
+ window.addEventListener('unload', Prototype.emptyFunction, false);
5622
+
5623
+
5624
+ var _getDOMEventName = Prototype.K,
5625
+ translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
5626
+
5627
+ if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
5628
+ _getDOMEventName = function(eventName) {
5629
+ return (translations[eventName] || eventName);
5630
+ };
5631
+ }
5632
+
5633
+ function observe(element, eventName, handler) {
5634
+ element = $(element);
5635
+
5636
+ var responder = _createResponder(element, eventName, handler);
5637
+
5638
+ if (!responder) return element;
5639
+
5640
+ if (eventName.include(':')) {
5641
+ if (element.addEventListener)
5642
+ element.addEventListener("dataavailable", responder, false);
5643
+ else {
5644
+ element.attachEvent("ondataavailable", responder);
5645
+ element.attachEvent("onlosecapture", responder);
5646
+ }
5647
+ } else {
5648
+ var actualEventName = _getDOMEventName(eventName);
5649
+
5650
+ if (element.addEventListener)
5651
+ element.addEventListener(actualEventName, responder, false);
5652
+ else
5653
+ element.attachEvent("on" + actualEventName, responder);
5654
+ }
5655
+
5656
+ return element;
5657
+ }
5658
+
5659
+ function stopObserving(element, eventName, handler) {
5660
+ element = $(element);
5661
+
5662
+ var registry = Element.retrieve(element, 'prototype_event_registry');
5663
+ if (!registry) return element;
5664
+
5665
+ if (!eventName) {
5666
+ registry.each( function(pair) {
5667
+ var eventName = pair.key;
5668
+ stopObserving(element, eventName);
5669
+ });
5670
+ return element;
5671
+ }
5672
+
5673
+ var responders = registry.get(eventName);
5674
+ if (!responders) return element;
5675
+
5676
+ if (!handler) {
5677
+ responders.each(function(r) {
5678
+ stopObserving(element, eventName, r.handler);
5679
+ });
5680
+ return element;
5681
+ }
5682
+
5683
+ var i = responders.length, responder;
5684
+ while (i--) {
5685
+ if (responders[i].handler === handler) {
5686
+ responder = responders[i];
5687
+ break;
5688
+ }
5689
+ }
5690
+ if (!responder) return element;
5691
+
5692
+ if (eventName.include(':')) {
5693
+ if (element.removeEventListener)
5694
+ element.removeEventListener("dataavailable", responder, false);
5695
+ else {
5696
+ element.detachEvent("ondataavailable", responder);
5697
+ element.detachEvent("onlosecapture", responder);
5698
+ }
5699
+ } else {
5700
+ var actualEventName = _getDOMEventName(eventName);
5701
+ if (element.removeEventListener)
5702
+ element.removeEventListener(actualEventName, responder, false);
5703
+ else
5704
+ element.detachEvent('on' + actualEventName, responder);
5705
+ }
5706
+
5707
+ registry.set(eventName, responders.without(responder));
5708
+
5709
+ return element;
5710
+ }
5711
+
5712
+ function fire(element, eventName, memo, bubble) {
5713
+ element = $(element);
5714
+
5715
+ if (Object.isUndefined(bubble))
5716
+ bubble = true;
5717
+
5718
+ if (element == document && document.createEvent && !element.dispatchEvent)
5719
+ element = document.documentElement;
5720
+
5721
+ var event;
5722
+ if (document.createEvent) {
5723
+ event = document.createEvent('HTMLEvents');
5724
+ event.initEvent('dataavailable', bubble, true);
5725
+ } else {
5726
+ event = document.createEventObject();
5727
+ event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
5728
+ }
5729
+
5730
+ event.eventName = eventName;
5731
+ event.memo = memo || { };
5732
+
5733
+ if (document.createEvent)
5734
+ element.dispatchEvent(event);
5735
+ else
5736
+ element.fireEvent(event.eventType, event);
5737
+
5738
+ return Event.extend(event);
5739
+ }
5740
+
5741
+ Event.Handler = Class.create({
5742
+ initialize: function(element, eventName, selector, callback) {
5743
+ this.element = $(element);
5744
+ this.eventName = eventName;
5745
+ this.selector = selector;
5746
+ this.callback = callback;
5747
+ this.handler = this.handleEvent.bind(this);
5748
+ },
5749
+
5750
+ start: function() {
5751
+ Event.observe(this.element, this.eventName, this.handler);
5752
+ return this;
5753
+ },
5754
+
5755
+ stop: function() {
5756
+ Event.stopObserving(this.element, this.eventName, this.handler);
5757
+ return this;
5758
+ },
5759
+
5760
+ handleEvent: function(event) {
5761
+ var element = Event.findElement(event, this.selector);
5762
+ if (element) this.callback.call(this.element, event, element);
5763
+ }
5764
+ });
5765
+
5766
+ function on(element, eventName, selector, callback) {
5767
+ element = $(element);
5768
+ if (Object.isFunction(selector) && Object.isUndefined(callback)) {
5769
+ callback = selector, selector = null;
5770
+ }
5771
+
5772
+ return new Event.Handler(element, eventName, selector, callback).start();
5773
+ }
5774
+
5775
+ Object.extend(Event, Event.Methods);
5776
+
5777
+ Object.extend(Event, {
5778
+ fire: fire,
5779
+ observe: observe,
5780
+ stopObserving: stopObserving,
5781
+ on: on
5782
+ });
5783
+
5784
+ Element.addMethods({
5785
+ fire: fire,
5786
+
5787
+ observe: observe,
5788
+
5789
+ stopObserving: stopObserving,
5790
+
5791
+ on: on
5792
+ });
5793
+
5794
+ Object.extend(document, {
5795
+ fire: fire.methodize(),
5796
+
5797
+ observe: observe.methodize(),
5798
+
5799
+ stopObserving: stopObserving.methodize(),
5800
+
5801
+ on: on.methodize(),
5802
+
5803
+ loaded: false
5804
+ });
5805
+
5806
+ if (window.Event) Object.extend(window.Event, Event);
5807
+ else window.Event = Event;
5808
+ })();
5809
+
5810
+ (function() {
5811
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
5812
+ Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
5813
+
5814
+ var timer;
5815
+
5816
+ function fireContentLoadedEvent() {
5817
+ if (document.loaded) return;
5818
+ if (timer) window.clearTimeout(timer);
5819
+ document.loaded = true;
5820
+ document.fire('dom:loaded');
5821
+ }
5822
+
5823
+ function checkReadyState() {
5824
+ if (document.readyState === 'complete') {
5825
+ document.stopObserving('readystatechange', checkReadyState);
5826
+ fireContentLoadedEvent();
5827
+ }
5828
+ }
5829
+
5830
+ function pollDoScroll() {
5831
+ try { document.documentElement.doScroll('left'); }
5832
+ catch(e) {
5833
+ timer = pollDoScroll.defer();
5834
+ return;
5835
+ }
5836
+ fireContentLoadedEvent();
5837
+ }
5838
+
5839
+ if (document.addEventListener) {
5840
+ document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
5841
+ } else {
5842
+ document.observe('readystatechange', checkReadyState);
5843
+ if (window == top)
5844
+ timer = pollDoScroll.defer();
5845
+ }
5846
+
5847
+ Event.observe(window, 'load', fireContentLoadedEvent);
5848
+ })();
5849
+
5850
+ Element.addMethods();
5851
+
5852
+ /*------------------------------- DEPRECATED -------------------------------*/
5853
+
5854
+ Hash.toQueryString = Object.toQueryString;
5855
+
5856
+ var Toggle = { display: Element.toggle };
5857
+
5858
+ Element.Methods.childOf = Element.Methods.descendantOf;
5859
+
5860
+ var Insertion = {
5861
+ Before: function(element, content) {
5862
+ return Element.insert(element, {before:content});
5863
+ },
5864
+
5865
+ Top: function(element, content) {
5866
+ return Element.insert(element, {top:content});
5867
+ },
5868
+
5869
+ Bottom: function(element, content) {
5870
+ return Element.insert(element, {bottom:content});
5871
+ },
5872
+
5873
+ After: function(element, content) {
5874
+ return Element.insert(element, {after:content});
5875
+ }
5876
+ };
5877
+
5878
+ var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
5879
+
5880
+ var Position = {
5881
+ includeScrollOffsets: false,
5882
+
5883
+ prepare: function() {
5884
+ this.deltaX = window.pageXOffset
5885
+ || document.documentElement.scrollLeft
5886
+ || document.body.scrollLeft
5887
+ || 0;
5888
+ this.deltaY = window.pageYOffset
5889
+ || document.documentElement.scrollTop
5890
+ || document.body.scrollTop
5891
+ || 0;
5892
+ },
5893
+
5894
+ within: function(element, x, y) {
5895
+ if (this.includeScrollOffsets)
5896
+ return this.withinIncludingScrolloffsets(element, x, y);
5897
+ this.xcomp = x;
5898
+ this.ycomp = y;
5899
+ this.offset = Element.cumulativeOffset(element);
5900
+
5901
+ return (y >= this.offset[1] &&
5902
+ y < this.offset[1] + element.offsetHeight &&
5903
+ x >= this.offset[0] &&
5904
+ x < this.offset[0] + element.offsetWidth);
5905
+ },
5906
+
5907
+ withinIncludingScrolloffsets: function(element, x, y) {
5908
+ var offsetcache = Element.cumulativeScrollOffset(element);
5909
+
5910
+ this.xcomp = x + offsetcache[0] - this.deltaX;
5911
+ this.ycomp = y + offsetcache[1] - this.deltaY;
5912
+ this.offset = Element.cumulativeOffset(element);
5913
+
5914
+ return (this.ycomp >= this.offset[1] &&
5915
+ this.ycomp < this.offset[1] + element.offsetHeight &&
5916
+ this.xcomp >= this.offset[0] &&
5917
+ this.xcomp < this.offset[0] + element.offsetWidth);
5918
+ },
5919
+
5920
+ overlap: function(mode, element) {
5921
+ if (!mode) return 0;
5922
+ if (mode == 'vertical')
5923
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
5924
+ element.offsetHeight;
5925
+ if (mode == 'horizontal')
5926
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
5927
+ element.offsetWidth;
5928
+ },
5929
+
5930
+
5931
+ cumulativeOffset: Element.Methods.cumulativeOffset,
5932
+
5933
+ positionedOffset: Element.Methods.positionedOffset,
5934
+
5935
+ absolutize: function(element) {
5936
+ Position.prepare();
5937
+ return Element.absolutize(element);
5938
+ },
5939
+
5940
+ relativize: function(element) {
5941
+ Position.prepare();
5942
+ return Element.relativize(element);
5943
+ },
5944
+
5945
+ realOffset: Element.Methods.cumulativeScrollOffset,
5946
+
5947
+ offsetParent: Element.Methods.getOffsetParent,
5948
+
5949
+ page: Element.Methods.viewportOffset,
5950
+
5951
+ clone: function(source, target, options) {
5952
+ options = options || { };
5953
+ return Element.clonePosition(target, source, options);
5954
+ }
5955
+ };
5956
+
5957
+ /*--------------------------------------------------------------------------*/
5958
+
5959
+ if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
5960
+ function iter(name) {
5961
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
5962
+ }
5963
+
5964
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
5965
+ function(element, className) {
5966
+ className = className.toString().strip();
5967
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
5968
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
5969
+ } : function(element, className) {
5970
+ className = className.toString().strip();
5971
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
5972
+ if (!classNames && !className) return elements;
5973
+
5974
+ var nodes = $(element).getElementsByTagName('*');
5975
+ className = ' ' + className + ' ';
5976
+
5977
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
5978
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
5979
+ (classNames && classNames.all(function(name) {
5980
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
5981
+ }))))
5982
+ elements.push(Element.extend(child));
5983
+ }
5984
+ return elements;
5985
+ };
5986
+
5987
+ return function(className, parentElement) {
5988
+ return $(parentElement || document.body).getElementsByClassName(className);
5989
+ };
5990
+ }(Element.Methods);
5991
+
5992
+ /*--------------------------------------------------------------------------*/
5993
+
5994
+ Element.ClassNames = Class.create();
5995
+ Element.ClassNames.prototype = {
5996
+ initialize: function(element) {
5997
+ this.element = $(element);
5998
+ },
5999
+
6000
+ _each: function(iterator) {
6001
+ this.element.className.split(/\s+/).select(function(name) {
6002
+ return name.length > 0;
6003
+ })._each(iterator);
6004
+ },
6005
+
6006
+ set: function(className) {
6007
+ this.element.className = className;
6008
+ },
6009
+
6010
+ add: function(classNameToAdd) {
6011
+ if (this.include(classNameToAdd)) return;
6012
+ this.set($A(this).concat(classNameToAdd).join(' '));
6013
+ },
6014
+
6015
+ remove: function(classNameToRemove) {
6016
+ if (!this.include(classNameToRemove)) return;
6017
+ this.set($A(this).without(classNameToRemove).join(' '));
6018
+ },
6019
+
6020
+ toString: function() {
6021
+ return $A(this).join(' ');
6022
+ }
6023
+ };
6024
+
6025
+ Object.extend(Element.ClassNames.prototype, Enumerable);
6026
+
6027
+ /*--------------------------------------------------------------------------*/
6028
+
6029
+ (function() {
6030
+ window.Selector = Class.create({
6031
+ initialize: function(expression) {
6032
+ this.expression = expression.strip();
6033
+ },
6034
+
6035
+ findElements: function(rootElement) {
6036
+ return Prototype.Selector.select(this.expression, rootElement);
6037
+ },
6038
+
6039
+ match: function(element) {
6040
+ return Prototype.Selector.match(element, this.expression);
6041
+ },
6042
+
6043
+ toString: function() {
6044
+ return this.expression;
6045
+ },
6046
+
6047
+ inspect: function() {
6048
+ return "#<Selector: " + this.expression + ">";
6049
+ }
6050
+ });
6051
+
6052
+ Object.extend(Selector, {
6053
+ matchElements: function(elements, expression) {
6054
+ var match = Prototype.Selector.match,
6055
+ results = [];
6056
+
6057
+ for (var i = 0, length = elements.length; i < length; i++) {
6058
+ var element = elements[i];
6059
+ if (match(element, expression)) {
6060
+ results.push(Element.extend(element));
6061
+ }
6062
+ }
6063
+ return results;
6064
+ },
6065
+
6066
+ findElement: function(elements, expression, index) {
6067
+ index = index || 0;
6068
+ var matchIndex = 0, element;
6069
+ for (var i = 0, length = elements.length; i < length; i++) {
6070
+ element = elements[i];
6071
+ if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
6072
+ return Element.extend(element);
6073
+ }
6074
+ }
6075
+ },
6076
+
6077
+ findChildElements: function(element, expressions) {
6078
+ var selector = expressions.toArray().join(', ');
6079
+ return Prototype.Selector.select(selector, element || document);
6080
+ }
6081
+ });
6082
+ })();
skin/adminhtml/default/default/wisepricer/prototype17.js ADDED
@@ -0,0 +1,6082 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Prototype JavaScript framework, version 1.7
2
+ * (c) 2005-2010 Sam Stephenson
3
+ *
4
+ * Prototype is freely distributable under the terms of an MIT-style license.
5
+ * For details, see the Prototype web site: http://www.prototypejs.org/
6
+ *
7
+ *--------------------------------------------------------------------------*/
8
+
9
+ var Prototype = {
10
+
11
+ Version: '1.7',
12
+
13
+ Browser: (function(){
14
+ var ua = navigator.userAgent;
15
+ var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
16
+ return {
17
+ IE: !!window.attachEvent && !isOpera,
18
+ Opera: isOpera,
19
+ WebKit: ua.indexOf('AppleWebKit/') > -1,
20
+ Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
21
+ MobileSafari: /Apple.*Mobile/.test(ua)
22
+ }
23
+ })(),
24
+
25
+ BrowserFeatures: {
26
+ XPath: !!document.evaluate,
27
+
28
+ SelectorsAPI: !!document.querySelector,
29
+
30
+ ElementExtensions: (function() {
31
+ var constructor = window.Element || window.HTMLElement;
32
+ return !!(constructor && constructor.prototype);
33
+ })(),
34
+ SpecificElementExtensions: (function() {
35
+ if (typeof window.HTMLDivElement !== 'undefined')
36
+ return true;
37
+
38
+ var div = document.createElement('div'),
39
+ form = document.createElement('form'),
40
+ isSupported = false;
41
+
42
+ if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
43
+ isSupported = true;
44
+ }
45
+
46
+ div = form = null;
47
+
48
+ return isSupported;
49
+ })()
50
+ },
51
+
52
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
53
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
54
+
55
+ emptyFunction: function() { },
56
+
57
+ K: function(x) { return x }
58
+ };
59
+
60
+ if (Prototype.Browser.MobileSafari)
61
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
62
+
63
+
64
+ var Abstract = { };
65
+
66
+
67
+ var Try = {
68
+ these: function() {
69
+ var returnValue;
70
+
71
+ for (var i = 0, length = arguments.length; i < length; i++) {
72
+ var lambda = arguments[i];
73
+ try {
74
+ returnValue = lambda();
75
+ break;
76
+ } catch (e) { }
77
+ }
78
+
79
+ return returnValue;
80
+ }
81
+ };
82
+
83
+ /* Based on Alex Arnell's inheritance implementation. */
84
+
85
+ var Class = (function() {
86
+
87
+ var IS_DONTENUM_BUGGY = (function(){
88
+ for (var p in { toString: 1 }) {
89
+ if (p === 'toString') return false;
90
+ }
91
+ return true;
92
+ })();
93
+
94
+ function subclass() {};
95
+ function create() {
96
+ var parent = null, properties = $A(arguments);
97
+ if (Object.isFunction(properties[0]))
98
+ parent = properties.shift();
99
+
100
+ function klass() {
101
+ this.initialize.apply(this, arguments);
102
+ }
103
+
104
+ Object.extend(klass, Class.Methods);
105
+ klass.superclass = parent;
106
+ klass.subclasses = [];
107
+
108
+ if (parent) {
109
+ subclass.prototype = parent.prototype;
110
+ klass.prototype = new subclass;
111
+ parent.subclasses.push(klass);
112
+ }
113
+
114
+ for (var i = 0, length = properties.length; i < length; i++)
115
+ klass.addMethods(properties[i]);
116
+
117
+ if (!klass.prototype.initialize)
118
+ klass.prototype.initialize = Prototype.emptyFunction;
119
+
120
+ klass.prototype.constructor = klass;
121
+ return klass;
122
+ }
123
+
124
+ function addMethods(source) {
125
+ var ancestor = this.superclass && this.superclass.prototype,
126
+ properties = Object.keys(source);
127
+
128
+ if (IS_DONTENUM_BUGGY) {
129
+ if (source.toString != Object.prototype.toString)
130
+ properties.push("toString");
131
+ if (source.valueOf != Object.prototype.valueOf)
132
+ properties.push("valueOf");
133
+ }
134
+
135
+ for (var i = 0, length = properties.length; i < length; i++) {
136
+ var property = properties[i], value = source[property];
137
+ if (ancestor && Object.isFunction(value) &&
138
+ value.argumentNames()[0] == "$super") {
139
+ var method = value;
140
+ value = (function(m) {
141
+ return function() { return ancestor[m].apply(this, arguments); };
142
+ })(property).wrap(method);
143
+
144
+ value.valueOf = method.valueOf.bind(method);
145
+ value.toString = method.toString.bind(method);
146
+ }
147
+ this.prototype[property] = value;
148
+ }
149
+
150
+ return this;
151
+ }
152
+
153
+ return {
154
+ create: create,
155
+ Methods: {
156
+ addMethods: addMethods
157
+ }
158
+ };
159
+ })();
160
+ (function() {
161
+
162
+ var _toString = Object.prototype.toString,
163
+ NULL_TYPE = 'Null',
164
+ UNDEFINED_TYPE = 'Undefined',
165
+ BOOLEAN_TYPE = 'Boolean',
166
+ NUMBER_TYPE = 'Number',
167
+ STRING_TYPE = 'String',
168
+ OBJECT_TYPE = 'Object',
169
+ FUNCTION_CLASS = '[object Function]',
170
+ BOOLEAN_CLASS = '[object Boolean]',
171
+ NUMBER_CLASS = '[object Number]',
172
+ STRING_CLASS = '[object String]',
173
+ ARRAY_CLASS = '[object Array]',
174
+ DATE_CLASS = '[object Date]',
175
+ NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
176
+ typeof JSON.stringify === 'function' &&
177
+ JSON.stringify(0) === '0' &&
178
+ typeof JSON.stringify(Prototype.K) === 'undefined';
179
+
180
+ function Type(o) {
181
+ switch(o) {
182
+ case null: return NULL_TYPE;
183
+ case (void 0): return UNDEFINED_TYPE;
184
+ }
185
+ var type = typeof o;
186
+ switch(type) {
187
+ case 'boolean': return BOOLEAN_TYPE;
188
+ case 'number': return NUMBER_TYPE;
189
+ case 'string': return STRING_TYPE;
190
+ }
191
+ return OBJECT_TYPE;
192
+ }
193
+
194
+ function extend(destination, source) {
195
+ for (var property in source)
196
+ destination[property] = source[property];
197
+ return destination;
198
+ }
199
+
200
+ function inspect(object) {
201
+ try {
202
+ if (isUndefined(object)) return 'undefined';
203
+ if (object === null) return 'null';
204
+ return object.inspect ? object.inspect() : String(object);
205
+ } catch (e) {
206
+ if (e instanceof RangeError) return '...';
207
+ throw e;
208
+ }
209
+ }
210
+
211
+ function toJSON(value) {
212
+ return Str('', { '': value }, []);
213
+ }
214
+
215
+ function Str(key, holder, stack) {
216
+ var value = holder[key],
217
+ type = typeof value;
218
+
219
+ if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
220
+ value = value.toJSON(key);
221
+ }
222
+
223
+ var _class = _toString.call(value);
224
+
225
+ switch (_class) {
226
+ case NUMBER_CLASS:
227
+ case BOOLEAN_CLASS:
228
+ case STRING_CLASS:
229
+ value = value.valueOf();
230
+ }
231
+
232
+ switch (value) {
233
+ case null: return 'null';
234
+ case true: return 'true';
235
+ case false: return 'false';
236
+ }
237
+
238
+ type = typeof value;
239
+ switch (type) {
240
+ case 'string':
241
+ return value.inspect(true);
242
+ case 'number':
243
+ return isFinite(value) ? String(value) : 'null';
244
+ case 'object':
245
+
246
+ for (var i = 0, length = stack.length; i < length; i++) {
247
+ if (stack[i] === value) { throw new TypeError(); }
248
+ }
249
+ stack.push(value);
250
+
251
+ var partial = [];
252
+ if (_class === ARRAY_CLASS) {
253
+ for (var i = 0, length = value.length; i < length; i++) {
254
+ var str = Str(i, value, stack);
255
+ partial.push(typeof str === 'undefined' ? 'null' : str);
256
+ }
257
+ partial = '[' + partial.join(',') + ']';
258
+ } else {
259
+ var keys = Object.keys(value);
260
+ for (var i = 0, length = keys.length; i < length; i++) {
261
+ var key = keys[i], str = Str(key, value, stack);
262
+ if (typeof str !== "undefined") {
263
+ partial.push(key.inspect(true)+ ':' + str);
264
+ }
265
+ }
266
+ partial = '{' + partial.join(',') + '}';
267
+ }
268
+ stack.pop();
269
+ return partial;
270
+ }
271
+ }
272
+
273
+ function stringify(object) {
274
+ return JSON.stringify(object);
275
+ }
276
+
277
+ function toQueryString(object) {
278
+ return $H(object).toQueryString();
279
+ }
280
+
281
+ function toHTML(object) {
282
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
283
+ }
284
+
285
+ function keys(object) {
286
+ if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
287
+ var results = [];
288
+ for (var property in object) {
289
+ if (object.hasOwnProperty(property)) {
290
+ results.push(property);
291
+ }
292
+ }
293
+ return results;
294
+ }
295
+
296
+ function values(object) {
297
+ var results = [];
298
+ for (var property in object)
299
+ results.push(object[property]);
300
+ return results;
301
+ }
302
+
303
+ function clone(object) {
304
+ return extend({ }, object);
305
+ }
306
+
307
+ function isElement(object) {
308
+ return !!(object && object.nodeType == 1);
309
+ }
310
+
311
+ function isArray(object) {
312
+ return _toString.call(object) === ARRAY_CLASS;
313
+ }
314
+
315
+ var hasNativeIsArray = (typeof Array.isArray == 'function')
316
+ && Array.isArray([]) && !Array.isArray({});
317
+
318
+ if (hasNativeIsArray) {
319
+ isArray = Array.isArray;
320
+ }
321
+
322
+ function isHash(object) {
323
+ return object instanceof Hash;
324
+ }
325
+
326
+ function isFunction(object) {
327
+ return _toString.call(object) === FUNCTION_CLASS;
328
+ }
329
+
330
+ function isString(object) {
331
+ return _toString.call(object) === STRING_CLASS;
332
+ }
333
+
334
+ function isNumber(object) {
335
+ return _toString.call(object) === NUMBER_CLASS;
336
+ }
337
+
338
+ function isDate(object) {
339
+ return _toString.call(object) === DATE_CLASS;
340
+ }
341
+
342
+ function isUndefined(object) {
343
+ return typeof object === "undefined";
344
+ }
345
+
346
+ extend(Object, {
347
+ extend: extend,
348
+ inspect: inspect,
349
+ toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
350
+ toQueryString: toQueryString,
351
+ toHTML: toHTML,
352
+ keys: Object.keys || keys,
353
+ values: values,
354
+ clone: clone,
355
+ isElement: isElement,
356
+ isArray: isArray,
357
+ isHash: isHash,
358
+ isFunction: isFunction,
359
+ isString: isString,
360
+ isNumber: isNumber,
361
+ isDate: isDate,
362
+ isUndefined: isUndefined
363
+ });
364
+ })();
365
+ Object.extend(Function.prototype, (function() {
366
+ var slice = Array.prototype.slice;
367
+
368
+ function update(array, args) {
369
+ var arrayLength = array.length, length = args.length;
370
+ while (length--) array[arrayLength + length] = args[length];
371
+ return array;
372
+ }
373
+
374
+ function merge(array, args) {
375
+ array = slice.call(array, 0);
376
+ return update(array, args);
377
+ }
378
+
379
+ function argumentNames() {
380
+ var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
381
+ .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
382
+ .replace(/\s+/g, '').split(',');
383
+ return names.length == 1 && !names[0] ? [] : names;
384
+ }
385
+
386
+ function bind(context) {
387
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
388
+ var __method = this, args = slice.call(arguments, 1);
389
+ return function() {
390
+ var a = merge(args, arguments);
391
+ return __method.apply(context, a);
392
+ }
393
+ }
394
+
395
+ function bindAsEventListener(context) {
396
+ var __method = this, args = slice.call(arguments, 1);
397
+ return function(event) {
398
+ var a = update([event || window.event], args);
399
+ return __method.apply(context, a);
400
+ }
401
+ }
402
+
403
+ function curry() {
404
+ if (!arguments.length) return this;
405
+ var __method = this, args = slice.call(arguments, 0);
406
+ return function() {
407
+ var a = merge(args, arguments);
408
+ return __method.apply(this, a);
409
+ }
410
+ }
411
+
412
+ function delay(timeout) {
413
+ var __method = this, args = slice.call(arguments, 1);
414
+ timeout = timeout * 1000;
415
+ return window.setTimeout(function() {
416
+ return __method.apply(__method, args);
417
+ }, timeout);
418
+ }
419
+
420
+ function defer() {
421
+ var args = update([0.01], arguments);
422
+ return this.delay.apply(this, args);
423
+ }
424
+
425
+ function wrap(wrapper) {
426
+ var __method = this;
427
+ return function() {
428
+ var a = update([__method.bind(this)], arguments);
429
+ return wrapper.apply(this, a);
430
+ }
431
+ }
432
+
433
+ function methodize() {
434
+ if (this._methodized) return this._methodized;
435
+ var __method = this;
436
+ return this._methodized = function() {
437
+ var a = update([this], arguments);
438
+ return __method.apply(null, a);
439
+ };
440
+ }
441
+
442
+ return {
443
+ argumentNames: argumentNames,
444
+ bind: bind,
445
+ bindAsEventListener: bindAsEventListener,
446
+ curry: curry,
447
+ delay: delay,
448
+ defer: defer,
449
+ wrap: wrap,
450
+ methodize: methodize
451
+ }
452
+ })());
453
+
454
+
455
+
456
+ (function(proto) {
457
+
458
+
459
+ function toISOString() {
460
+ return this.getUTCFullYear() + '-' +
461
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
462
+ this.getUTCDate().toPaddedString(2) + 'T' +
463
+ this.getUTCHours().toPaddedString(2) + ':' +
464
+ this.getUTCMinutes().toPaddedString(2) + ':' +
465
+ this.getUTCSeconds().toPaddedString(2) + 'Z';
466
+ }
467
+
468
+
469
+ function toJSON() {
470
+ return this.toISOString();
471
+ }
472
+
473
+ if (!proto.toISOString) proto.toISOString = toISOString;
474
+ if (!proto.toJSON) proto.toJSON = toJSON;
475
+
476
+ })(Date.prototype);
477
+
478
+
479
+ RegExp.prototype.match = RegExp.prototype.test;
480
+
481
+ RegExp.escape = function(str) {
482
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
483
+ };
484
+ var PeriodicalExecuter = Class.create({
485
+ initialize: function(callback, frequency) {
486
+ this.callback = callback;
487
+ this.frequency = frequency;
488
+ this.currentlyExecuting = false;
489
+
490
+ this.registerCallback();
491
+ },
492
+
493
+ registerCallback: function() {
494
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
495
+ },
496
+
497
+ execute: function() {
498
+ this.callback(this);
499
+ },
500
+
501
+ stop: function() {
502
+ if (!this.timer) return;
503
+ clearInterval(this.timer);
504
+ this.timer = null;
505
+ },
506
+
507
+ onTimerEvent: function() {
508
+ if (!this.currentlyExecuting) {
509
+ try {
510
+ this.currentlyExecuting = true;
511
+ this.execute();
512
+ this.currentlyExecuting = false;
513
+ } catch(e) {
514
+ this.currentlyExecuting = false;
515
+ throw e;
516
+ }
517
+ }
518
+ }
519
+ });
520
+ Object.extend(String, {
521
+ interpret: function(value) {
522
+ return value == null ? '' : String(value);
523
+ },
524
+ specialChar: {
525
+ '\b': '\\b',
526
+ '\t': '\\t',
527
+ '\n': '\\n',
528
+ '\f': '\\f',
529
+ '\r': '\\r',
530
+ '\\': '\\\\'
531
+ }
532
+ });
533
+
534
+ Object.extend(String.prototype, (function() {
535
+ var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
536
+ typeof JSON.parse === 'function' &&
537
+ JSON.parse('{"test": true}').test;
538
+
539
+ function prepareReplacement(replacement) {
540
+ if (Object.isFunction(replacement)) return replacement;
541
+ var template = new Template(replacement);
542
+ return function(match) { return template.evaluate(match) };
543
+ }
544
+
545
+ function gsub(pattern, replacement) {
546
+ var result = '', source = this, match;
547
+ replacement = prepareReplacement(replacement);
548
+
549
+ if (Object.isString(pattern))
550
+ pattern = RegExp.escape(pattern);
551
+
552
+ if (!(pattern.length || pattern.source)) {
553
+ replacement = replacement('');
554
+ return replacement + source.split('').join(replacement) + replacement;
555
+ }
556
+
557
+ while (source.length > 0) {
558
+ if (match = source.match(pattern)) {
559
+ result += source.slice(0, match.index);
560
+ result += String.interpret(replacement(match));
561
+ source = source.slice(match.index + match[0].length);
562
+ } else {
563
+ result += source, source = '';
564
+ }
565
+ }
566
+ return result;
567
+ }
568
+
569
+ function sub(pattern, replacement, count) {
570
+ replacement = prepareReplacement(replacement);
571
+ count = Object.isUndefined(count) ? 1 : count;
572
+
573
+ return this.gsub(pattern, function(match) {
574
+ if (--count < 0) return match[0];
575
+ return replacement(match);
576
+ });
577
+ }
578
+
579
+ function scan(pattern, iterator) {
580
+ this.gsub(pattern, iterator);
581
+ return String(this);
582
+ }
583
+
584
+ function truncate(length, truncation) {
585
+ length = length || 30;
586
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
587
+ return this.length > length ?
588
+ this.slice(0, length - truncation.length) + truncation : String(this);
589
+ }
590
+
591
+ function strip() {
592
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
593
+ }
594
+
595
+ function stripTags() {
596
+ return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
597
+ }
598
+
599
+ function stripScripts() {
600
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
601
+ }
602
+
603
+ function extractScripts() {
604
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
605
+ matchOne = new RegExp(Prototype.ScriptFragment, 'im');
606
+ return (this.match(matchAll) || []).map(function(scriptTag) {
607
+ return (scriptTag.match(matchOne) || ['', ''])[1];
608
+ });
609
+ }
610
+
611
+ function evalScripts() {
612
+ return this.extractScripts().map(function(script) { return eval(script) });
613
+ }
614
+
615
+ function escapeHTML() {
616
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
617
+ }
618
+
619
+ function unescapeHTML() {
620
+ return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
621
+ }
622
+
623
+
624
+ function toQueryParams(separator) {
625
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
626
+ if (!match) return { };
627
+
628
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
629
+ if ((pair = pair.split('='))[0]) {
630
+ var key = decodeURIComponent(pair.shift()),
631
+ value = pair.length > 1 ? pair.join('=') : pair[0];
632
+
633
+ if (value != undefined) value = decodeURIComponent(value);
634
+
635
+ if (key in hash) {
636
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
637
+ hash[key].push(value);
638
+ }
639
+ else hash[key] = value;
640
+ }
641
+ return hash;
642
+ });
643
+ }
644
+
645
+ function toArray() {
646
+ return this.split('');
647
+ }
648
+
649
+ function succ() {
650
+ return this.slice(0, this.length - 1) +
651
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
652
+ }
653
+
654
+ function times(count) {
655
+ return count < 1 ? '' : new Array(count + 1).join(this);
656
+ }
657
+
658
+ function camelize() {
659
+ return this.replace(/-+(.)?/g, function(match, chr) {
660
+ return chr ? chr.toUpperCase() : '';
661
+ });
662
+ }
663
+
664
+ function capitalize() {
665
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
666
+ }
667
+
668
+ function underscore() {
669
+ return this.replace(/::/g, '/')
670
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
671
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
672
+ .replace(/-/g, '_')
673
+ .toLowerCase();
674
+ }
675
+
676
+ function dasherize() {
677
+ return this.replace(/_/g, '-');
678
+ }
679
+
680
+ function inspect(useDoubleQuotes) {
681
+ var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
682
+ if (character in String.specialChar) {
683
+ return String.specialChar[character];
684
+ }
685
+ return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
686
+ });
687
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
688
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
689
+ }
690
+
691
+ function unfilterJSON(filter) {
692
+ return this.replace(filter || Prototype.JSONFilter, '$1');
693
+ }
694
+
695
+ function isJSON() {
696
+ var str = this;
697
+ if (str.blank()) return false;
698
+ str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
699
+ str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
700
+ str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
701
+ return (/^[\],:{}\s]*$/).test(str);
702
+ }
703
+
704
+ function evalJSON(sanitize) {
705
+ var json = this.unfilterJSON(),
706
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
707
+ if (cx.test(json)) {
708
+ json = json.replace(cx, function (a) {
709
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
710
+ });
711
+ }
712
+ try {
713
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
714
+ } catch (e) { }
715
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
716
+ }
717
+
718
+ function parseJSON() {
719
+ var json = this.unfilterJSON();
720
+ return JSON.parse(json);
721
+ }
722
+
723
+ function include(pattern) {
724
+ return this.indexOf(pattern) > -1;
725
+ }
726
+
727
+ function startsWith(pattern) {
728
+ return this.lastIndexOf(pattern, 0) === 0;
729
+ }
730
+
731
+ function endsWith(pattern) {
732
+ var d = this.length - pattern.length;
733
+ return d >= 0 && this.indexOf(pattern, d) === d;
734
+ }
735
+
736
+ function empty() {
737
+ return this == '';
738
+ }
739
+
740
+ function blank() {
741
+ return /^\s*$/.test(this);
742
+ }
743
+
744
+ function interpolate(object, pattern) {
745
+ return new Template(this, pattern).evaluate(object);
746
+ }
747
+
748
+ return {
749
+ gsub: gsub,
750
+ sub: sub,
751
+ scan: scan,
752
+ truncate: truncate,
753
+ strip: String.prototype.trim || strip,
754
+ stripTags: stripTags,
755
+ stripScripts: stripScripts,
756
+ extractScripts: extractScripts,
757
+ evalScripts: evalScripts,
758
+ escapeHTML: escapeHTML,
759
+ unescapeHTML: unescapeHTML,
760
+ toQueryParams: toQueryParams,
761
+ parseQuery: toQueryParams,
762
+ toArray: toArray,
763
+ succ: succ,
764
+ times: times,
765
+ camelize: camelize,
766
+ capitalize: capitalize,
767
+ underscore: underscore,
768
+ dasherize: dasherize,
769
+ inspect: inspect,
770
+ unfilterJSON: unfilterJSON,
771
+ isJSON: isJSON,
772
+ evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
773
+ include: include,
774
+ startsWith: startsWith,
775
+ endsWith: endsWith,
776
+ empty: empty,
777
+ blank: blank,
778
+ interpolate: interpolate
779
+ };
780
+ })());
781
+
782
+ var Template = Class.create({
783
+ initialize: function(template, pattern) {
784
+ this.template = template.toString();
785
+ this.pattern = pattern || Template.Pattern;
786
+ },
787
+
788
+ evaluate: function(object) {
789
+ if (object && Object.isFunction(object.toTemplateReplacements))
790
+ object = object.toTemplateReplacements();
791
+
792
+ return this.template.gsub(this.pattern, function(match) {
793
+ if (object == null) return (match[1] + '');
794
+
795
+ var before = match[1] || '';
796
+ if (before == '\\') return match[2];
797
+
798
+ var ctx = object, expr = match[3],
799
+ pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
800
+
801
+ match = pattern.exec(expr);
802
+ if (match == null) return before;
803
+
804
+ while (match != null) {
805
+ var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
806
+ ctx = ctx[comp];
807
+ if (null == ctx || '' == match[3]) break;
808
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
809
+ match = pattern.exec(expr);
810
+ }
811
+
812
+ return before + String.interpret(ctx);
813
+ });
814
+ }
815
+ });
816
+ Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
817
+
818
+ var $break = { };
819
+
820
+ var Enumerable = (function() {
821
+ function each(iterator, context) {
822
+ var index = 0;
823
+ try {
824
+ this._each(function(value) {
825
+ iterator.call(context, value, index++);
826
+ });
827
+ } catch (e) {
828
+ if (e != $break) throw e;
829
+ }
830
+ return this;
831
+ }
832
+
833
+ function eachSlice(number, iterator, context) {
834
+ var index = -number, slices = [], array = this.toArray();
835
+ if (number < 1) return array;
836
+ while ((index += number) < array.length)
837
+ slices.push(array.slice(index, index+number));
838
+ return slices.collect(iterator, context);
839
+ }
840
+
841
+ function all(iterator, context) {
842
+ iterator = iterator || Prototype.K;
843
+ var result = true;
844
+ this.each(function(value, index) {
845
+ result = result && !!iterator.call(context, value, index);
846
+ if (!result) throw $break;
847
+ });
848
+ return result;
849
+ }
850
+
851
+ function any(iterator, context) {
852
+ iterator = iterator || Prototype.K;
853
+ var result = false;
854
+ this.each(function(value, index) {
855
+ if (result = !!iterator.call(context, value, index))
856
+ throw $break;
857
+ });
858
+ return result;
859
+ }
860
+
861
+ function collect(iterator, context) {
862
+ iterator = iterator || Prototype.K;
863
+ var results = [];
864
+ this.each(function(value, index) {
865
+ results.push(iterator.call(context, value, index));
866
+ });
867
+ return results;
868
+ }
869
+
870
+ function detect(iterator, context) {
871
+ var result;
872
+ this.each(function(value, index) {
873
+ if (iterator.call(context, value, index)) {
874
+ result = value;
875
+ throw $break;
876
+ }
877
+ });
878
+ return result;
879
+ }
880
+
881
+ function findAll(iterator, context) {
882
+ var results = [];
883
+ this.each(function(value, index) {
884
+ if (iterator.call(context, value, index))
885
+ results.push(value);
886
+ });
887
+ return results;
888
+ }
889
+
890
+ function grep(filter, iterator, context) {
891
+ iterator = iterator || Prototype.K;
892
+ var results = [];
893
+
894
+ if (Object.isString(filter))
895
+ filter = new RegExp(RegExp.escape(filter));
896
+
897
+ this.each(function(value, index) {
898
+ if (filter.match(value))
899
+ results.push(iterator.call(context, value, index));
900
+ });
901
+ return results;
902
+ }
903
+
904
+ function include(object) {
905
+ if (Object.isFunction(this.indexOf))
906
+ if (this.indexOf(object) != -1) return true;
907
+
908
+ var found = false;
909
+ this.each(function(value) {
910
+ if (value == object) {
911
+ found = true;
912
+ throw $break;
913
+ }
914
+ });
915
+ return found;
916
+ }
917
+
918
+ function inGroupsOf(number, fillWith) {
919
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
920
+ return this.eachSlice(number, function(slice) {
921
+ while(slice.length < number) slice.push(fillWith);
922
+ return slice;
923
+ });
924
+ }
925
+
926
+ function inject(memo, iterator, context) {
927
+ this.each(function(value, index) {
928
+ memo = iterator.call(context, memo, value, index);
929
+ });
930
+ return memo;
931
+ }
932
+
933
+ function invoke(method) {
934
+ var args = $A(arguments).slice(1);
935
+ return this.map(function(value) {
936
+ return value[method].apply(value, args);
937
+ });
938
+ }
939
+
940
+ function max(iterator, context) {
941
+ iterator = iterator || Prototype.K;
942
+ var result;
943
+ this.each(function(value, index) {
944
+ value = iterator.call(context, value, index);
945
+ if (result == null || value >= result)
946
+ result = value;
947
+ });
948
+ return result;
949
+ }
950
+
951
+ function min(iterator, context) {
952
+ iterator = iterator || Prototype.K;
953
+ var result;
954
+ this.each(function(value, index) {
955
+ value = iterator.call(context, value, index);
956
+ if (result == null || value < result)
957
+ result = value;
958
+ });
959
+ return result;
960
+ }
961
+
962
+ function partition(iterator, context) {
963
+ iterator = iterator || Prototype.K;
964
+ var trues = [], falses = [];
965
+ this.each(function(value, index) {
966
+ (iterator.call(context, value, index) ?
967
+ trues : falses).push(value);
968
+ });
969
+ return [trues, falses];
970
+ }
971
+
972
+ function pluck(property) {
973
+ var results = [];
974
+ this.each(function(value) {
975
+ results.push(value[property]);
976
+ });
977
+ return results;
978
+ }
979
+
980
+ function reject(iterator, context) {
981
+ var results = [];
982
+ this.each(function(value, index) {
983
+ if (!iterator.call(context, value, index))
984
+ results.push(value);
985
+ });
986
+ return results;
987
+ }
988
+
989
+ function sortBy(iterator, context) {
990
+ return this.map(function(value, index) {
991
+ return {
992
+ value: value,
993
+ criteria: iterator.call(context, value, index)
994
+ };
995
+ }).sort(function(left, right) {
996
+ var a = left.criteria, b = right.criteria;
997
+ return a < b ? -1 : a > b ? 1 : 0;
998
+ }).pluck('value');
999
+ }
1000
+
1001
+ function toArray() {
1002
+ return this.map();
1003
+ }
1004
+
1005
+ function zip() {
1006
+ var iterator = Prototype.K, args = $A(arguments);
1007
+ if (Object.isFunction(args.last()))
1008
+ iterator = args.pop();
1009
+
1010
+ var collections = [this].concat(args).map($A);
1011
+ return this.map(function(value, index) {
1012
+ return iterator(collections.pluck(index));
1013
+ });
1014
+ }
1015
+
1016
+ function size() {
1017
+ return this.toArray().length;
1018
+ }
1019
+
1020
+ function inspect() {
1021
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
1022
+ }
1023
+
1024
+
1025
+
1026
+
1027
+
1028
+
1029
+
1030
+
1031
+
1032
+ return {
1033
+ each: each,
1034
+ eachSlice: eachSlice,
1035
+ all: all,
1036
+ every: all,
1037
+ any: any,
1038
+ some: any,
1039
+ collect: collect,
1040
+ map: collect,
1041
+ detect: detect,
1042
+ findAll: findAll,
1043
+ select: findAll,
1044
+ filter: findAll,
1045
+ grep: grep,
1046
+ include: include,
1047
+ member: include,
1048
+ inGroupsOf: inGroupsOf,
1049
+ inject: inject,
1050
+ invoke: invoke,
1051
+ max: max,
1052
+ min: min,
1053
+ partition: partition,
1054
+ pluck: pluck,
1055
+ reject: reject,
1056
+ sortBy: sortBy,
1057
+ toArray: toArray,
1058
+ entries: toArray,
1059
+ zip: zip,
1060
+ size: size,
1061
+ inspect: inspect,
1062
+ find: detect
1063
+ };
1064
+ })();
1065
+
1066
+ function $A(iterable) {
1067
+ if (!iterable) return [];
1068
+ if ('toArray' in Object(iterable)) return iterable.toArray();
1069
+ var length = iterable.length || 0, results = new Array(length);
1070
+ while (length--) results[length] = iterable[length];
1071
+ return results;
1072
+ }
1073
+
1074
+
1075
+ function $w(string) {
1076
+ if (!Object.isString(string)) return [];
1077
+ string = string.strip();
1078
+ return string ? string.split(/\s+/) : [];
1079
+ }
1080
+
1081
+ Array.from = $A;
1082
+
1083
+
1084
+ (function() {
1085
+ var arrayProto = Array.prototype,
1086
+ slice = arrayProto.slice,
1087
+ _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
1088
+
1089
+ function each(iterator, context) {
1090
+ for (var i = 0, length = this.length >>> 0; i < length; i++) {
1091
+ if (i in this) iterator.call(context, this[i], i, this);
1092
+ }
1093
+ }
1094
+ if (!_each) _each = each;
1095
+
1096
+ function clear() {
1097
+ this.length = 0;
1098
+ return this;
1099
+ }
1100
+
1101
+ function first() {
1102
+ return this[0];
1103
+ }
1104
+
1105
+ function last() {
1106
+ return this[this.length - 1];
1107
+ }
1108
+
1109
+ function compact() {
1110
+ return this.select(function(value) {
1111
+ return value != null;
1112
+ });
1113
+ }
1114
+
1115
+ function flatten() {
1116
+ return this.inject([], function(array, value) {
1117
+ if (Object.isArray(value))
1118
+ return array.concat(value.flatten());
1119
+ array.push(value);
1120
+ return array;
1121
+ });
1122
+ }
1123
+
1124
+ function without() {
1125
+ var values = slice.call(arguments, 0);
1126
+ return this.select(function(value) {
1127
+ return !values.include(value);
1128
+ });
1129
+ }
1130
+
1131
+ function reverse(inline) {
1132
+ return (inline === false ? this.toArray() : this)._reverse();
1133
+ }
1134
+
1135
+ function uniq(sorted) {
1136
+ return this.inject([], function(array, value, index) {
1137
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1138
+ array.push(value);
1139
+ return array;
1140
+ });
1141
+ }
1142
+
1143
+ function intersect(array) {
1144
+ return this.uniq().findAll(function(item) {
1145
+ return array.detect(function(value) { return item === value });
1146
+ });
1147
+ }
1148
+
1149
+
1150
+ function clone() {
1151
+ return slice.call(this, 0);
1152
+ }
1153
+
1154
+ function size() {
1155
+ return this.length;
1156
+ }
1157
+
1158
+ function inspect() {
1159
+ return '[' + this.map(Object.inspect).join(', ') + ']';
1160
+ }
1161
+
1162
+ function indexOf(item, i) {
1163
+ i || (i = 0);
1164
+ var length = this.length;
1165
+ if (i < 0) i = length + i;
1166
+ for (; i < length; i++)
1167
+ if (this[i] === item) return i;
1168
+ return -1;
1169
+ }
1170
+
1171
+ function lastIndexOf(item, i) {
1172
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
1173
+ var n = this.slice(0, i).reverse().indexOf(item);
1174
+ return (n < 0) ? n : i - n - 1;
1175
+ }
1176
+
1177
+ function concat() {
1178
+ var array = slice.call(this, 0), item;
1179
+ for (var i = 0, length = arguments.length; i < length; i++) {
1180
+ item = arguments[i];
1181
+ if (Object.isArray(item) && !('callee' in item)) {
1182
+ for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
1183
+ array.push(item[j]);
1184
+ } else {
1185
+ array.push(item);
1186
+ }
1187
+ }
1188
+ return array;
1189
+ }
1190
+
1191
+ Object.extend(arrayProto, Enumerable);
1192
+
1193
+ if (!arrayProto._reverse)
1194
+ arrayProto._reverse = arrayProto.reverse;
1195
+
1196
+ Object.extend(arrayProto, {
1197
+ _each: _each,
1198
+ clear: clear,
1199
+ first: first,
1200
+ last: last,
1201
+ compact: compact,
1202
+ flatten: flatten,
1203
+ without: without,
1204
+ reverse: reverse,
1205
+ uniq: uniq,
1206
+ intersect: intersect,
1207
+ clone: clone,
1208
+ toArray: clone,
1209
+ size: size,
1210
+ inspect: inspect
1211
+ });
1212
+
1213
+ var CONCAT_ARGUMENTS_BUGGY = (function() {
1214
+ return [].concat(arguments)[0][0] !== 1;
1215
+ })(1,2)
1216
+
1217
+ if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1218
+
1219
+ if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1220
+ if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1221
+ })();
1222
+ function $H(object) {
1223
+ return new Hash(object);
1224
+ };
1225
+
1226
+ var Hash = Class.create(Enumerable, (function() {
1227
+ function initialize(object) {
1228
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1229
+ }
1230
+
1231
+
1232
+ function _each(iterator) {
1233
+ for (var key in this._object) {
1234
+ var value = this._object[key], pair = [key, value];
1235
+ pair.key = key;
1236
+ pair.value = value;
1237
+ iterator(pair);
1238
+ }
1239
+ }
1240
+
1241
+ function set(key, value) {
1242
+ return this._object[key] = value;
1243
+ }
1244
+
1245
+ function get(key) {
1246
+ if (this._object[key] !== Object.prototype[key])
1247
+ return this._object[key];
1248
+ }
1249
+
1250
+ function unset(key) {
1251
+ var value = this._object[key];
1252
+ delete this._object[key];
1253
+ return value;
1254
+ }
1255
+
1256
+ function toObject() {
1257
+ return Object.clone(this._object);
1258
+ }
1259
+
1260
+
1261
+
1262
+ function keys() {
1263
+ return this.pluck('key');
1264
+ }
1265
+
1266
+ function values() {
1267
+ return this.pluck('value');
1268
+ }
1269
+
1270
+ function index(value) {
1271
+ var match = this.detect(function(pair) {
1272
+ return pair.value === value;
1273
+ });
1274
+ return match && match.key;
1275
+ }
1276
+
1277
+ function merge(object) {
1278
+ return this.clone().update(object);
1279
+ }
1280
+
1281
+ function update(object) {
1282
+ return new Hash(object).inject(this, function(result, pair) {
1283
+ result.set(pair.key, pair.value);
1284
+ return result;
1285
+ });
1286
+ }
1287
+
1288
+ function toQueryPair(key, value) {
1289
+ if (Object.isUndefined(value)) return key;
1290
+ return key + '=' + encodeURIComponent(String.interpret(value));
1291
+ }
1292
+
1293
+ function toQueryString() {
1294
+ return this.inject([], function(results, pair) {
1295
+ var key = encodeURIComponent(pair.key), values = pair.value;
1296
+
1297
+ if (values && typeof values == 'object') {
1298
+ if (Object.isArray(values)) {
1299
+ var queryValues = [];
1300
+ for (var i = 0, len = values.length, value; i < len; i++) {
1301
+ value = values[i];
1302
+ queryValues.push(toQueryPair(key, value));
1303
+ }
1304
+ return results.concat(queryValues);
1305
+ }
1306
+ } else results.push(toQueryPair(key, values));
1307
+ return results;
1308
+ }).join('&');
1309
+ }
1310
+
1311
+ function inspect() {
1312
+ return '#<Hash:{' + this.map(function(pair) {
1313
+ return pair.map(Object.inspect).join(': ');
1314
+ }).join(', ') + '}>';
1315
+ }
1316
+
1317
+ function clone() {
1318
+ return new Hash(this);
1319
+ }
1320
+
1321
+ return {
1322
+ initialize: initialize,
1323
+ _each: _each,
1324
+ set: set,
1325
+ get: get,
1326
+ unset: unset,
1327
+ toObject: toObject,
1328
+ toTemplateReplacements: toObject,
1329
+ keys: keys,
1330
+ values: values,
1331
+ index: index,
1332
+ merge: merge,
1333
+ update: update,
1334
+ toQueryString: toQueryString,
1335
+ inspect: inspect,
1336
+ toJSON: toObject,
1337
+ clone: clone
1338
+ };
1339
+ })());
1340
+
1341
+ Hash.from = $H;
1342
+ Object.extend(Number.prototype, (function() {
1343
+ function toColorPart() {
1344
+ return this.toPaddedString(2, 16);
1345
+ }
1346
+
1347
+ function succ() {
1348
+ return this + 1;
1349
+ }
1350
+
1351
+ function times(iterator, context) {
1352
+ $R(0, this, true).each(iterator, context);
1353
+ return this;
1354
+ }
1355
+
1356
+ function toPaddedString(length, radix) {
1357
+ var string = this.toString(radix || 10);
1358
+ return '0'.times(length - string.length) + string;
1359
+ }
1360
+
1361
+ function abs() {
1362
+ return Math.abs(this);
1363
+ }
1364
+
1365
+ function round() {
1366
+ return Math.round(this);
1367
+ }
1368
+
1369
+ function ceil() {
1370
+ return Math.ceil(this);
1371
+ }
1372
+
1373
+ function floor() {
1374
+ return Math.floor(this);
1375
+ }
1376
+
1377
+ return {
1378
+ toColorPart: toColorPart,
1379
+ succ: succ,
1380
+ times: times,
1381
+ toPaddedString: toPaddedString,
1382
+ abs: abs,
1383
+ round: round,
1384
+ ceil: ceil,
1385
+ floor: floor
1386
+ };
1387
+ })());
1388
+
1389
+ function $R(start, end, exclusive) {
1390
+ return new ObjectRange(start, end, exclusive);
1391
+ }
1392
+
1393
+ var ObjectRange = Class.create(Enumerable, (function() {
1394
+ function initialize(start, end, exclusive) {
1395
+ this.start = start;
1396
+ this.end = end;
1397
+ this.exclusive = exclusive;
1398
+ }
1399
+
1400
+ function _each(iterator) {
1401
+ var value = this.start;
1402
+ while (this.include(value)) {
1403
+ iterator(value);
1404
+ value = value.succ();
1405
+ }
1406
+ }
1407
+
1408
+ function include(value) {
1409
+ if (value < this.start)
1410
+ return false;
1411
+ if (this.exclusive)
1412
+ return value < this.end;
1413
+ return value <= this.end;
1414
+ }
1415
+
1416
+ return {
1417
+ initialize: initialize,
1418
+ _each: _each,
1419
+ include: include
1420
+ };
1421
+ })());
1422
+
1423
+
1424
+
1425
+ var Ajax = {
1426
+ getTransport: function() {
1427
+ return Try.these(
1428
+ function() {return new XMLHttpRequest()},
1429
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1430
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1431
+ ) || false;
1432
+ },
1433
+
1434
+ activeRequestCount: 0
1435
+ };
1436
+
1437
+ Ajax.Responders = {
1438
+ responders: [],
1439
+
1440
+ _each: function(iterator) {
1441
+ this.responders._each(iterator);
1442
+ },
1443
+
1444
+ register: function(responder) {
1445
+ if (!this.include(responder))
1446
+ this.responders.push(responder);
1447
+ },
1448
+
1449
+ unregister: function(responder) {
1450
+ this.responders = this.responders.without(responder);
1451
+ },
1452
+
1453
+ dispatch: function(callback, request, transport, json) {
1454
+ this.each(function(responder) {
1455
+ if (Object.isFunction(responder[callback])) {
1456
+ try {
1457
+ responder[callback].apply(responder, [request, transport, json]);
1458
+ } catch (e) { }
1459
+ }
1460
+ });
1461
+ }
1462
+ };
1463
+
1464
+ Object.extend(Ajax.Responders, Enumerable);
1465
+
1466
+ Ajax.Responders.register({
1467
+ onCreate: function() { Ajax.activeRequestCount++ },
1468
+ onComplete: function() { Ajax.activeRequestCount-- }
1469
+ });
1470
+ Ajax.Base = Class.create({
1471
+ initialize: function(options) {
1472
+ this.options = {
1473
+ method: 'post',
1474
+ asynchronous: true,
1475
+ contentType: 'application/x-www-form-urlencoded',
1476
+ encoding: 'UTF-8',
1477
+ parameters: '',
1478
+ evalJSON: true,
1479
+ evalJS: true
1480
+ };
1481
+ Object.extend(this.options, options || { });
1482
+
1483
+ this.options.method = this.options.method.toLowerCase();
1484
+
1485
+ if (Object.isHash(this.options.parameters))
1486
+ this.options.parameters = this.options.parameters.toObject();
1487
+ }
1488
+ });
1489
+ Ajax.Request = Class.create(Ajax.Base, {
1490
+ _complete: false,
1491
+
1492
+ initialize: function($super, url, options) {
1493
+ $super(options);
1494
+ this.transport = Ajax.getTransport();
1495
+ this.request(url);
1496
+ },
1497
+
1498
+ request: function(url) {
1499
+ this.url = url;
1500
+ this.method = this.options.method;
1501
+ var params = Object.isString(this.options.parameters) ?
1502
+ this.options.parameters :
1503
+ Object.toQueryString(this.options.parameters);
1504
+
1505
+ if (!['get', 'post'].include(this.method)) {
1506
+ params += (params ? '&' : '') + "_method=" + this.method;
1507
+ this.method = 'post';
1508
+ }
1509
+
1510
+ if (params && this.method === 'get') {
1511
+ this.url += (this.url.include('?') ? '&' : '?') + params;
1512
+ }
1513
+
1514
+ this.parameters = params.toQueryParams();
1515
+
1516
+ try {
1517
+ var response = new Ajax.Response(this);
1518
+ if (this.options.onCreate) this.options.onCreate(response);
1519
+ Ajax.Responders.dispatch('onCreate', this, response);
1520
+
1521
+ this.transport.open(this.method.toUpperCase(), this.url,
1522
+ this.options.asynchronous);
1523
+
1524
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1525
+
1526
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
1527
+ this.setRequestHeaders();
1528
+
1529
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1530
+ this.transport.send(this.body);
1531
+
1532
+ /* Force Firefox to handle ready state 4 for synchronous requests */
1533
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
1534
+ this.onStateChange();
1535
+
1536
+ }
1537
+ catch (e) {
1538
+ this.dispatchException(e);
1539
+ }
1540
+ },
1541
+
1542
+ onStateChange: function() {
1543
+ var readyState = this.transport.readyState;
1544
+ if (readyState > 1 && !((readyState == 4) && this._complete))
1545
+ this.respondToReadyState(this.transport.readyState);
1546
+ },
1547
+
1548
+ setRequestHeaders: function() {
1549
+ var headers = {
1550
+ 'X-Requested-With': 'XMLHttpRequest',
1551
+ 'X-Prototype-Version': Prototype.Version,
1552
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1553
+ };
1554
+
1555
+ if (this.method == 'post') {
1556
+ headers['Content-type'] = this.options.contentType +
1557
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
1558
+
1559
+ /* Force "Connection: close" for older Mozilla browsers to work
1560
+ * around a bug where XMLHttpRequest sends an incorrect
1561
+ * Content-length header. See Mozilla Bugzilla #246651.
1562
+ */
1563
+ if (this.transport.overrideMimeType &&
1564
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1565
+ headers['Connection'] = 'close';
1566
+ }
1567
+
1568
+ if (typeof this.options.requestHeaders == 'object') {
1569
+ var extras = this.options.requestHeaders;
1570
+
1571
+ if (Object.isFunction(extras.push))
1572
+ for (var i = 0, length = extras.length; i < length; i += 2)
1573
+ headers[extras[i]] = extras[i+1];
1574
+ else
1575
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1576
+ }
1577
+
1578
+ for (var name in headers)
1579
+ this.transport.setRequestHeader(name, headers[name]);
1580
+ },
1581
+
1582
+ success: function() {
1583
+ var status = this.getStatus();
1584
+ return !status || (status >= 200 && status < 300) || status == 304;
1585
+ },
1586
+
1587
+ getStatus: function() {
1588
+ try {
1589
+ if (this.transport.status === 1223) return 204;
1590
+ return this.transport.status || 0;
1591
+ } catch (e) { return 0 }
1592
+ },
1593
+
1594
+ respondToReadyState: function(readyState) {
1595
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1596
+
1597
+ if (state == 'Complete') {
1598
+ try {
1599
+ this._complete = true;
1600
+ (this.options['on' + response.status]
1601
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1602
+ || Prototype.emptyFunction)(response, response.headerJSON);
1603
+ } catch (e) {
1604
+ this.dispatchException(e);
1605
+ }
1606
+
1607
+ var contentType = response.getHeader('Content-type');
1608
+ if (this.options.evalJS == 'force'
1609
+ || (this.options.evalJS && this.isSameOrigin() && contentType
1610
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1611
+ this.evalResponse();
1612
+ }
1613
+
1614
+ try {
1615
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1616
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1617
+ } catch (e) {
1618
+ this.dispatchException(e);
1619
+ }
1620
+
1621
+ if (state == 'Complete') {
1622
+ this.transport.onreadystatechange = Prototype.emptyFunction;
1623
+ }
1624
+ },
1625
+
1626
+ isSameOrigin: function() {
1627
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1628
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1629
+ protocol: location.protocol,
1630
+ domain: document.domain,
1631
+ port: location.port ? ':' + location.port : ''
1632
+ }));
1633
+ },
1634
+
1635
+ getHeader: function(name) {
1636
+ try {
1637
+ return this.transport.getResponseHeader(name) || null;
1638
+ } catch (e) { return null; }
1639
+ },
1640
+
1641
+ evalResponse: function() {
1642
+ try {
1643
+ return eval((this.transport.responseText || '').unfilterJSON());
1644
+ } catch (e) {
1645
+ this.dispatchException(e);
1646
+ }
1647
+ },
1648
+
1649
+ dispatchException: function(exception) {
1650
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
1651
+ Ajax.Responders.dispatch('onException', this, exception);
1652
+ }
1653
+ });
1654
+
1655
+ Ajax.Request.Events =
1656
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1657
+
1658
+
1659
+
1660
+
1661
+
1662
+
1663
+
1664
+
1665
+ Ajax.Response = Class.create({
1666
+ initialize: function(request){
1667
+ this.request = request;
1668
+ var transport = this.transport = request.transport,
1669
+ readyState = this.readyState = transport.readyState;
1670
+
1671
+ if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1672
+ this.status = this.getStatus();
1673
+ this.statusText = this.getStatusText();
1674
+ this.responseText = String.interpret(transport.responseText);
1675
+ this.headerJSON = this._getHeaderJSON();
1676
+ }
1677
+
1678
+ if (readyState == 4) {
1679
+ var xml = transport.responseXML;
1680
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
1681
+ this.responseJSON = this._getResponseJSON();
1682
+ }
1683
+ },
1684
+
1685
+ status: 0,
1686
+
1687
+ statusText: '',
1688
+
1689
+ getStatus: Ajax.Request.prototype.getStatus,
1690
+
1691
+ getStatusText: function() {
1692
+ try {
1693
+ return this.transport.statusText || '';
1694
+ } catch (e) { return '' }
1695
+ },
1696
+
1697
+ getHeader: Ajax.Request.prototype.getHeader,
1698
+
1699
+ getAllHeaders: function() {
1700
+ try {
1701
+ return this.getAllResponseHeaders();
1702
+ } catch (e) { return null }
1703
+ },
1704
+
1705
+ getResponseHeader: function(name) {
1706
+ return this.transport.getResponseHeader(name);
1707
+ },
1708
+
1709
+ getAllResponseHeaders: function() {
1710
+ return this.transport.getAllResponseHeaders();
1711
+ },
1712
+
1713
+ _getHeaderJSON: function() {
1714
+ var json = this.getHeader('X-JSON');
1715
+ if (!json) return null;
1716
+ json = decodeURIComponent(escape(json));
1717
+ try {
1718
+ return json.evalJSON(this.request.options.sanitizeJSON ||
1719
+ !this.request.isSameOrigin());
1720
+ } catch (e) {
1721
+ this.request.dispatchException(e);
1722
+ }
1723
+ },
1724
+
1725
+ _getResponseJSON: function() {
1726
+ var options = this.request.options;
1727
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
1728
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
1729
+ this.responseText.blank())
1730
+ return null;
1731
+ try {
1732
+ return this.responseText.evalJSON(options.sanitizeJSON ||
1733
+ !this.request.isSameOrigin());
1734
+ } catch (e) {
1735
+ this.request.dispatchException(e);
1736
+ }
1737
+ }
1738
+ });
1739
+
1740
+ Ajax.Updater = Class.create(Ajax.Request, {
1741
+ initialize: function($super, container, url, options) {
1742
+ this.container = {
1743
+ success: (container.success || container),
1744
+ failure: (container.failure || (container.success ? null : container))
1745
+ };
1746
+
1747
+ options = Object.clone(options);
1748
+ var onComplete = options.onComplete;
1749
+ options.onComplete = (function(response, json) {
1750
+ this.updateContent(response.responseText);
1751
+ if (Object.isFunction(onComplete)) onComplete(response, json);
1752
+ }).bind(this);
1753
+
1754
+ $super(url, options);
1755
+ },
1756
+
1757
+ updateContent: function(responseText) {
1758
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
1759
+ options = this.options;
1760
+
1761
+ if (!options.evalScripts) responseText = responseText.stripScripts();
1762
+
1763
+ if (receiver = $(receiver)) {
1764
+ if (options.insertion) {
1765
+ if (Object.isString(options.insertion)) {
1766
+ var insertion = { }; insertion[options.insertion] = responseText;
1767
+ receiver.insert(insertion);
1768
+ }
1769
+ else options.insertion(receiver, responseText);
1770
+ }
1771
+ else receiver.update(responseText);
1772
+ }
1773
+ }
1774
+ });
1775
+
1776
+ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1777
+ initialize: function($super, container, url, options) {
1778
+ $super(options);
1779
+ this.onComplete = this.options.onComplete;
1780
+
1781
+ this.frequency = (this.options.frequency || 2);
1782
+ this.decay = (this.options.decay || 1);
1783
+
1784
+ this.updater = { };
1785
+ this.container = container;
1786
+ this.url = url;
1787
+
1788
+ this.start();
1789
+ },
1790
+
1791
+ start: function() {
1792
+ this.options.onComplete = this.updateComplete.bind(this);
1793
+ this.onTimerEvent();
1794
+ },
1795
+
1796
+ stop: function() {
1797
+ this.updater.options.onComplete = undefined;
1798
+ clearTimeout(this.timer);
1799
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1800
+ },
1801
+
1802
+ updateComplete: function(response) {
1803
+ if (this.options.decay) {
1804
+ this.decay = (response.responseText == this.lastText ?
1805
+ this.decay * this.options.decay : 1);
1806
+
1807
+ this.lastText = response.responseText;
1808
+ }
1809
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1810
+ },
1811
+
1812
+ onTimerEvent: function() {
1813
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
1814
+ }
1815
+ });
1816
+
1817
+
1818
+ function $(element) {
1819
+ if (arguments.length > 1) {
1820
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1821
+ elements.push($(arguments[i]));
1822
+ return elements;
1823
+ }
1824
+ if (Object.isString(element))
1825
+ element = document.getElementById(element);
1826
+ return Element.extend(element);
1827
+ }
1828
+
1829
+ if (Prototype.BrowserFeatures.XPath) {
1830
+ document._getElementsByXPath = function(expression, parentElement) {
1831
+ var results = [];
1832
+ var query = document.evaluate(expression, $(parentElement) || document,
1833
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1834
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
1835
+ results.push(Element.extend(query.snapshotItem(i)));
1836
+ return results;
1837
+ };
1838
+ }
1839
+
1840
+ /*--------------------------------------------------------------------------*/
1841
+
1842
+ if (!Node) var Node = { };
1843
+
1844
+ if (!Node.ELEMENT_NODE) {
1845
+ Object.extend(Node, {
1846
+ ELEMENT_NODE: 1,
1847
+ ATTRIBUTE_NODE: 2,
1848
+ TEXT_NODE: 3,
1849
+ CDATA_SECTION_NODE: 4,
1850
+ ENTITY_REFERENCE_NODE: 5,
1851
+ ENTITY_NODE: 6,
1852
+ PROCESSING_INSTRUCTION_NODE: 7,
1853
+ COMMENT_NODE: 8,
1854
+ DOCUMENT_NODE: 9,
1855
+ DOCUMENT_TYPE_NODE: 10,
1856
+ DOCUMENT_FRAGMENT_NODE: 11,
1857
+ NOTATION_NODE: 12
1858
+ });
1859
+ }
1860
+
1861
+
1862
+
1863
+ (function(global) {
1864
+ function shouldUseCache(tagName, attributes) {
1865
+ if (tagName === 'select') return false;
1866
+ if ('type' in attributes) return false;
1867
+ return true;
1868
+ }
1869
+
1870
+ var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
1871
+ try {
1872
+ var el = document.createElement('<input name="x">');
1873
+ return el.tagName.toLowerCase() === 'input' && el.name === 'x';
1874
+ }
1875
+ catch(err) {
1876
+ return false;
1877
+ }
1878
+ })();
1879
+
1880
+ var element = global.Element;
1881
+
1882
+ global.Element = function(tagName, attributes) {
1883
+ attributes = attributes || { };
1884
+ tagName = tagName.toLowerCase();
1885
+ var cache = Element.cache;
1886
+
1887
+ if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
1888
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
1889
+ delete attributes.name;
1890
+ return Element.writeAttribute(document.createElement(tagName), attributes);
1891
+ }
1892
+
1893
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1894
+
1895
+ var node = shouldUseCache(tagName, attributes) ?
1896
+ cache[tagName].cloneNode(false) : document.createElement(tagName);
1897
+
1898
+ return Element.writeAttribute(node, attributes);
1899
+ };
1900
+
1901
+ Object.extend(global.Element, element || { });
1902
+ if (element) global.Element.prototype = element.prototype;
1903
+
1904
+ })(this);
1905
+
1906
+ Element.idCounter = 1;
1907
+ Element.cache = { };
1908
+
1909
+ Element._purgeElement = function(element) {
1910
+ var uid = element._prototypeUID;
1911
+ if (uid) {
1912
+ Element.stopObserving(element);
1913
+ element._prototypeUID = void 0;
1914
+ delete Element.Storage[uid];
1915
+ }
1916
+ }
1917
+
1918
+ Element.Methods = {
1919
+ visible: function(element) {
1920
+ return $(element).style.display != 'none';
1921
+ },
1922
+
1923
+ toggle: function(element) {
1924
+ element = $(element);
1925
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
1926
+ return element;
1927
+ },
1928
+
1929
+ hide: function(element) {
1930
+ element = $(element);
1931
+ element.style.display = 'none';
1932
+ return element;
1933
+ },
1934
+
1935
+ show: function(element) {
1936
+ element = $(element);
1937
+ element.style.display = '';
1938
+ return element;
1939
+ },
1940
+
1941
+ remove: function(element) {
1942
+ element = $(element);
1943
+ element.parentNode.removeChild(element);
1944
+ return element;
1945
+ },
1946
+
1947
+ update: (function(){
1948
+
1949
+ var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
1950
+ var el = document.createElement("select"),
1951
+ isBuggy = true;
1952
+ el.innerHTML = "<option value=\"test\">test</option>";
1953
+ if (el.options && el.options[0]) {
1954
+ isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
1955
+ }
1956
+ el = null;
1957
+ return isBuggy;
1958
+ })();
1959
+
1960
+ var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
1961
+ try {
1962
+ var el = document.createElement("table");
1963
+ if (el && el.tBodies) {
1964
+ el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
1965
+ var isBuggy = typeof el.tBodies[0] == "undefined";
1966
+ el = null;
1967
+ return isBuggy;
1968
+ }
1969
+ } catch (e) {
1970
+ return true;
1971
+ }
1972
+ })();
1973
+
1974
+ var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
1975
+ try {
1976
+ var el = document.createElement('div');
1977
+ el.innerHTML = "<link>";
1978
+ var isBuggy = (el.childNodes.length === 0);
1979
+ el = null;
1980
+ return isBuggy;
1981
+ } catch(e) {
1982
+ return true;
1983
+ }
1984
+ })();
1985
+
1986
+ var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
1987
+ TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
1988
+
1989
+ var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
1990
+ var s = document.createElement("script"),
1991
+ isBuggy = false;
1992
+ try {
1993
+ s.appendChild(document.createTextNode(""));
1994
+ isBuggy = !s.firstChild ||
1995
+ s.firstChild && s.firstChild.nodeType !== 3;
1996
+ } catch (e) {
1997
+ isBuggy = true;
1998
+ }
1999
+ s = null;
2000
+ return isBuggy;
2001
+ })();
2002
+
2003
+
2004
+ function update(element, content) {
2005
+ element = $(element);
2006
+ var purgeElement = Element._purgeElement;
2007
+
2008
+ var descendants = element.getElementsByTagName('*'),
2009
+ i = descendants.length;
2010
+ while (i--) purgeElement(descendants[i]);
2011
+
2012
+ if (content && content.toElement)
2013
+ content = content.toElement();
2014
+
2015
+ if (Object.isElement(content))
2016
+ return element.update().insert(content);
2017
+
2018
+ content = Object.toHTML(content);
2019
+
2020
+ var tagName = element.tagName.toUpperCase();
2021
+
2022
+ if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
2023
+ element.text = content;
2024
+ return element;
2025
+ }
2026
+
2027
+ if (ANY_INNERHTML_BUGGY) {
2028
+ if (tagName in Element._insertionTranslations.tags) {
2029
+ while (element.firstChild) {
2030
+ element.removeChild(element.firstChild);
2031
+ }
2032
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2033
+ .each(function(node) {
2034
+ element.appendChild(node)
2035
+ });
2036
+ } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
2037
+ while (element.firstChild) {
2038
+ element.removeChild(element.firstChild);
2039
+ }
2040
+ var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
2041
+ nodes.each(function(node) { element.appendChild(node) });
2042
+ }
2043
+ else {
2044
+ element.innerHTML = content.stripScripts();
2045
+ }
2046
+ }
2047
+ else {
2048
+ element.innerHTML = content.stripScripts();
2049
+ }
2050
+
2051
+ content.evalScripts.bind(content).defer();
2052
+ return element;
2053
+ }
2054
+
2055
+ return update;
2056
+ })(),
2057
+
2058
+ replace: function(element, content) {
2059
+ element = $(element);
2060
+ if (content && content.toElement) content = content.toElement();
2061
+ else if (!Object.isElement(content)) {
2062
+ content = Object.toHTML(content);
2063
+ var range = element.ownerDocument.createRange();
2064
+ range.selectNode(element);
2065
+ content.evalScripts.bind(content).defer();
2066
+ content = range.createContextualFragment(content.stripScripts());
2067
+ }
2068
+ element.parentNode.replaceChild(content, element);
2069
+ return element;
2070
+ },
2071
+
2072
+ insert: function(element, insertions) {
2073
+ element = $(element);
2074
+
2075
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
2076
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2077
+ insertions = {bottom:insertions};
2078
+
2079
+ var content, insert, tagName, childNodes;
2080
+
2081
+ for (var position in insertions) {
2082
+ content = insertions[position];
2083
+ position = position.toLowerCase();
2084
+ insert = Element._insertionTranslations[position];
2085
+
2086
+ if (content && content.toElement) content = content.toElement();
2087
+ if (Object.isElement(content)) {
2088
+ insert(element, content);
2089
+ continue;
2090
+ }
2091
+
2092
+ content = Object.toHTML(content);
2093
+
2094
+ tagName = ((position == 'before' || position == 'after')
2095
+ ? element.parentNode : element).tagName.toUpperCase();
2096
+
2097
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2098
+
2099
+ if (position == 'top' || position == 'after') childNodes.reverse();
2100
+ childNodes.each(insert.curry(element));
2101
+
2102
+ content.evalScripts.bind(content).defer();
2103
+ }
2104
+
2105
+ return element;
2106
+ },
2107
+
2108
+ wrap: function(element, wrapper, attributes) {
2109
+ element = $(element);
2110
+ if (Object.isElement(wrapper))
2111
+ $(wrapper).writeAttribute(attributes || { });
2112
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
2113
+ else wrapper = new Element('div', wrapper);
2114
+ if (element.parentNode)
2115
+ element.parentNode.replaceChild(wrapper, element);
2116
+ wrapper.appendChild(element);
2117
+ return wrapper;
2118
+ },
2119
+
2120
+ inspect: function(element) {
2121
+ element = $(element);
2122
+ var result = '<' + element.tagName.toLowerCase();
2123
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
2124
+ var property = pair.first(),
2125
+ attribute = pair.last(),
2126
+ value = (element[property] || '').toString();
2127
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
2128
+ });
2129
+ return result + '>';
2130
+ },
2131
+
2132
+ recursivelyCollect: function(element, property, maximumLength) {
2133
+ element = $(element);
2134
+ maximumLength = maximumLength || -1;
2135
+ var elements = [];
2136
+
2137
+ while (element = element[property]) {
2138
+ if (element.nodeType == 1)
2139
+ elements.push(Element.extend(element));
2140
+ if (elements.length == maximumLength)
2141
+ break;
2142
+ }
2143
+
2144
+ return elements;
2145
+ },
2146
+
2147
+ ancestors: function(element) {
2148
+ return Element.recursivelyCollect(element, 'parentNode');
2149
+ },
2150
+
2151
+ descendants: function(element) {
2152
+ return Element.select(element, "*");
2153
+ },
2154
+
2155
+ firstDescendant: function(element) {
2156
+ element = $(element).firstChild;
2157
+ while (element && element.nodeType != 1) element = element.nextSibling;
2158
+ return $(element);
2159
+ },
2160
+
2161
+ immediateDescendants: function(element) {
2162
+ var results = [], child = $(element).firstChild;
2163
+ while (child) {
2164
+ if (child.nodeType === 1) {
2165
+ results.push(Element.extend(child));
2166
+ }
2167
+ child = child.nextSibling;
2168
+ }
2169
+ return results;
2170
+ },
2171
+
2172
+ previousSiblings: function(element, maximumLength) {
2173
+ return Element.recursivelyCollect(element, 'previousSibling');
2174
+ },
2175
+
2176
+ nextSiblings: function(element) {
2177
+ return Element.recursivelyCollect(element, 'nextSibling');
2178
+ },
2179
+
2180
+ siblings: function(element) {
2181
+ element = $(element);
2182
+ return Element.previousSiblings(element).reverse()
2183
+ .concat(Element.nextSiblings(element));
2184
+ },
2185
+
2186
+ match: function(element, selector) {
2187
+ element = $(element);
2188
+ if (Object.isString(selector))
2189
+ return Prototype.Selector.match(element, selector);
2190
+ return selector.match(element);
2191
+ },
2192
+
2193
+ up: function(element, expression, index) {
2194
+ element = $(element);
2195
+ if (arguments.length == 1) return $(element.parentNode);
2196
+ var ancestors = Element.ancestors(element);
2197
+ return Object.isNumber(expression) ? ancestors[expression] :
2198
+ Prototype.Selector.find(ancestors, expression, index);
2199
+ },
2200
+
2201
+ down: function(element, expression, index) {
2202
+ element = $(element);
2203
+ if (arguments.length == 1) return Element.firstDescendant(element);
2204
+ return Object.isNumber(expression) ? Element.descendants(element)[expression] :
2205
+ Element.select(element, expression)[index || 0];
2206
+ },
2207
+
2208
+ previous: function(element, expression, index) {
2209
+ element = $(element);
2210
+ if (Object.isNumber(expression)) index = expression, expression = false;
2211
+ if (!Object.isNumber(index)) index = 0;
2212
+
2213
+ if (expression) {
2214
+ return Prototype.Selector.find(element.previousSiblings(), expression, index);
2215
+ } else {
2216
+ return element.recursivelyCollect("previousSibling", index + 1)[index];
2217
+ }
2218
+ },
2219
+
2220
+ next: function(element, expression, index) {
2221
+ element = $(element);
2222
+ if (Object.isNumber(expression)) index = expression, expression = false;
2223
+ if (!Object.isNumber(index)) index = 0;
2224
+
2225
+ if (expression) {
2226
+ return Prototype.Selector.find(element.nextSiblings(), expression, index);
2227
+ } else {
2228
+ var maximumLength = Object.isNumber(index) ? index + 1 : 1;
2229
+ return element.recursivelyCollect("nextSibling", index + 1)[index];
2230
+ }
2231
+ },
2232
+
2233
+
2234
+ select: function(element) {
2235
+ element = $(element);
2236
+ var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2237
+ return Prototype.Selector.select(expressions, element);
2238
+ },
2239
+
2240
+ adjacent: function(element) {
2241
+ element = $(element);
2242
+ var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2243
+ return Prototype.Selector.select(expressions, element.parentNode).without(element);
2244
+ },
2245
+
2246
+ identify: function(element) {
2247
+ element = $(element);
2248
+ var id = Element.readAttribute(element, 'id');
2249
+ if (id) return id;
2250
+ do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
2251
+ Element.writeAttribute(element, 'id', id);
2252
+ return id;
2253
+ },
2254
+
2255
+ readAttribute: function(element, name) {
2256
+ element = $(element);
2257
+ if (Prototype.Browser.IE) {
2258
+ var t = Element._attributeTranslations.read;
2259
+ if (t.values[name]) return t.values[name](element, name);
2260
+ if (t.names[name]) name = t.names[name];
2261
+ if (name.include(':')) {
2262
+ return (!element.attributes || !element.attributes[name]) ? null :
2263
+ element.attributes[name].value;
2264
+ }
2265
+ }
2266
+ return element.getAttribute(name);
2267
+ },
2268
+
2269
+ writeAttribute: function(element, name, value) {
2270
+ element = $(element);
2271
+ var attributes = { }, t = Element._attributeTranslations.write;
2272
+
2273
+ if (typeof name == 'object') attributes = name;
2274
+ else attributes[name] = Object.isUndefined(value) ? true : value;
2275
+
2276
+ for (var attr in attributes) {
2277
+ name = t.names[attr] || attr;
2278
+ value = attributes[attr];
2279
+ if (t.values[attr]) name = t.values[attr](element, value);
2280
+ if (value === false || value === null)
2281
+ element.removeAttribute(name);
2282
+ else if (value === true)
2283
+ element.setAttribute(name, name);
2284
+ else element.setAttribute(name, value);
2285
+ }
2286
+ return element;
2287
+ },
2288
+
2289
+ getHeight: function(element) {
2290
+ return Element.getDimensions(element).height;
2291
+ },
2292
+
2293
+ getWidth: function(element) {
2294
+ return Element.getDimensions(element).width;
2295
+ },
2296
+
2297
+ classNames: function(element) {
2298
+ return new Element.ClassNames(element);
2299
+ },
2300
+
2301
+ hasClassName: function(element, className) {
2302
+ if (!(element = $(element))) return;
2303
+ var elementClassName = element.className;
2304
+ return (elementClassName.length > 0 && (elementClassName == className ||
2305
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
2306
+ },
2307
+
2308
+ addClassName: function(element, className) {
2309
+ if (!(element = $(element))) return;
2310
+ if (!Element.hasClassName(element, className))
2311
+ element.className += (element.className ? ' ' : '') + className;
2312
+ return element;
2313
+ },
2314
+
2315
+ removeClassName: function(element, className) {
2316
+ if (!(element = $(element))) return;
2317
+ element.className = element.className.replace(
2318
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
2319
+ return element;
2320
+ },
2321
+
2322
+ toggleClassName: function(element, className) {
2323
+ if (!(element = $(element))) return;
2324
+ return Element[Element.hasClassName(element, className) ?
2325
+ 'removeClassName' : 'addClassName'](element, className);
2326
+ },
2327
+
2328
+ cleanWhitespace: function(element) {
2329
+ element = $(element);
2330
+ var node = element.firstChild;
2331
+ while (node) {
2332
+ var nextNode = node.nextSibling;
2333
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
2334
+ element.removeChild(node);
2335
+ node = nextNode;
2336
+ }
2337
+ return element;
2338
+ },
2339
+
2340
+ empty: function(element) {
2341
+ return $(element).innerHTML.blank();
2342
+ },
2343
+
2344
+ descendantOf: function(element, ancestor) {
2345
+ element = $(element), ancestor = $(ancestor);
2346
+
2347
+ if (element.compareDocumentPosition)
2348
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
2349
+
2350
+ if (ancestor.contains)
2351
+ return ancestor.contains(element) && ancestor !== element;
2352
+
2353
+ while (element = element.parentNode)
2354
+ if (element == ancestor) return true;
2355
+
2356
+ return false;
2357
+ },
2358
+
2359
+ scrollTo: function(element) {
2360
+ element = $(element);
2361
+ var pos = Element.cumulativeOffset(element);
2362
+ window.scrollTo(pos[0], pos[1]);
2363
+ return element;
2364
+ },
2365
+
2366
+ getStyle: function(element, style) {
2367
+ element = $(element);
2368
+ style = style == 'float' ? 'cssFloat' : style.camelize();
2369
+ var value = element.style[style];
2370
+ if (!value || value == 'auto') {
2371
+ var css = document.defaultView.getComputedStyle(element, null);
2372
+ value = css ? css[style] : null;
2373
+ }
2374
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
2375
+ return value == 'auto' ? null : value;
2376
+ },
2377
+
2378
+ getOpacity: function(element) {
2379
+ return $(element).getStyle('opacity');
2380
+ },
2381
+
2382
+ setStyle: function(element, styles) {
2383
+ element = $(element);
2384
+ var elementStyle = element.style, match;
2385
+ if (Object.isString(styles)) {
2386
+ element.style.cssText += ';' + styles;
2387
+ return styles.include('opacity') ?
2388
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
2389
+ }
2390
+ for (var property in styles)
2391
+ if (property == 'opacity') element.setOpacity(styles[property]);
2392
+ else
2393
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
2394
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
2395
+ property] = styles[property];
2396
+
2397
+ return element;
2398
+ },
2399
+
2400
+ setOpacity: function(element, value) {
2401
+ element = $(element);
2402
+ element.style.opacity = (value == 1 || value === '') ? '' :
2403
+ (value < 0.00001) ? 0 : value;
2404
+ return element;
2405
+ },
2406
+
2407
+ makePositioned: function(element) {
2408
+ element = $(element);
2409
+ var pos = Element.getStyle(element, 'position');
2410
+ if (pos == 'static' || !pos) {
2411
+ element._madePositioned = true;
2412
+ element.style.position = 'relative';
2413
+ if (Prototype.Browser.Opera) {
2414
+ element.style.top = 0;
2415
+ element.style.left = 0;
2416
+ }
2417
+ }
2418
+ return element;
2419
+ },
2420
+
2421
+ undoPositioned: function(element) {
2422
+ element = $(element);
2423
+ if (element._madePositioned) {
2424
+ element._madePositioned = undefined;
2425
+ element.style.position =
2426
+ element.style.top =
2427
+ element.style.left =
2428
+ element.style.bottom =
2429
+ element.style.right = '';
2430
+ }
2431
+ return element;
2432
+ },
2433
+
2434
+ makeClipping: function(element) {
2435
+ element = $(element);
2436
+ if (element._overflow) return element;
2437
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
2438
+ if (element._overflow !== 'hidden')
2439
+ element.style.overflow = 'hidden';
2440
+ return element;
2441
+ },
2442
+
2443
+ undoClipping: function(element) {
2444
+ element = $(element);
2445
+ if (!element._overflow) return element;
2446
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2447
+ element._overflow = null;
2448
+ return element;
2449
+ },
2450
+
2451
+ clonePosition: function(element, source) {
2452
+ var options = Object.extend({
2453
+ setLeft: true,
2454
+ setTop: true,
2455
+ setWidth: true,
2456
+ setHeight: true,
2457
+ offsetTop: 0,
2458
+ offsetLeft: 0
2459
+ }, arguments[2] || { });
2460
+
2461
+ source = $(source);
2462
+ var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
2463
+
2464
+ element = $(element);
2465
+
2466
+ if (Element.getStyle(element, 'position') == 'absolute') {
2467
+ parent = Element.getOffsetParent(element);
2468
+ delta = Element.viewportOffset(parent);
2469
+ }
2470
+
2471
+ if (parent == document.body) {
2472
+ delta[0] -= document.body.offsetLeft;
2473
+ delta[1] -= document.body.offsetTop;
2474
+ }
2475
+
2476
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2477
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2478
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
2479
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2480
+ return element;
2481
+ }
2482
+ };
2483
+
2484
+ Object.extend(Element.Methods, {
2485
+ getElementsBySelector: Element.Methods.select,
2486
+
2487
+ childElements: Element.Methods.immediateDescendants
2488
+ });
2489
+
2490
+ Element._attributeTranslations = {
2491
+ write: {
2492
+ names: {
2493
+ className: 'class',
2494
+ htmlFor: 'for'
2495
+ },
2496
+ values: { }
2497
+ }
2498
+ };
2499
+
2500
+ if (Prototype.Browser.Opera) {
2501
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2502
+ function(proceed, element, style) {
2503
+ switch (style) {
2504
+ case 'height': case 'width':
2505
+ if (!Element.visible(element)) return null;
2506
+
2507
+ var dim = parseInt(proceed(element, style), 10);
2508
+
2509
+ if (dim !== element['offset' + style.capitalize()])
2510
+ return dim + 'px';
2511
+
2512
+ var properties;
2513
+ if (style === 'height') {
2514
+ properties = ['border-top-width', 'padding-top',
2515
+ 'padding-bottom', 'border-bottom-width'];
2516
+ }
2517
+ else {
2518
+ properties = ['border-left-width', 'padding-left',
2519
+ 'padding-right', 'border-right-width'];
2520
+ }
2521
+ return properties.inject(dim, function(memo, property) {
2522
+ var val = proceed(element, property);
2523
+ return val === null ? memo : memo - parseInt(val, 10);
2524
+ }) + 'px';
2525
+ default: return proceed(element, style);
2526
+ }
2527
+ }
2528
+ );
2529
+
2530
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2531
+ function(proceed, element, attribute) {
2532
+ if (attribute === 'title') return element.title;
2533
+ return proceed(element, attribute);
2534
+ }
2535
+ );
2536
+ }
2537
+
2538
+ else if (Prototype.Browser.IE) {
2539
+ Element.Methods.getStyle = function(element, style) {
2540
+ element = $(element);
2541
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2542
+ var value = element.style[style];
2543
+ if (!value && element.currentStyle) value = element.currentStyle[style];
2544
+
2545
+ if (style == 'opacity') {
2546
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2547
+ if (value[1]) return parseFloat(value[1]) / 100;
2548
+ return 1.0;
2549
+ }
2550
+
2551
+ if (value == 'auto') {
2552
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2553
+ return element['offset' + style.capitalize()] + 'px';
2554
+ return null;
2555
+ }
2556
+ return value;
2557
+ };
2558
+
2559
+ Element.Methods.setOpacity = function(element, value) {
2560
+ function stripAlpha(filter){
2561
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
2562
+ }
2563
+ element = $(element);
2564
+ var currentStyle = element.currentStyle;
2565
+ if ((currentStyle && !currentStyle.hasLayout) ||
2566
+ (!currentStyle && element.style.zoom == 'normal'))
2567
+ element.style.zoom = 1;
2568
+
2569
+ var filter = element.getStyle('filter'), style = element.style;
2570
+ if (value == 1 || value === '') {
2571
+ (filter = stripAlpha(filter)) ?
2572
+ style.filter = filter : style.removeAttribute('filter');
2573
+ return element;
2574
+ } else if (value < 0.00001) value = 0;
2575
+ style.filter = stripAlpha(filter) +
2576
+ 'alpha(opacity=' + (value * 100) + ')';
2577
+ return element;
2578
+ };
2579
+
2580
+ Element._attributeTranslations = (function(){
2581
+
2582
+ var classProp = 'className',
2583
+ forProp = 'for',
2584
+ el = document.createElement('div');
2585
+
2586
+ el.setAttribute(classProp, 'x');
2587
+
2588
+ if (el.className !== 'x') {
2589
+ el.setAttribute('class', 'x');
2590
+ if (el.className === 'x') {
2591
+ classProp = 'class';
2592
+ }
2593
+ }
2594
+ el = null;
2595
+
2596
+ el = document.createElement('label');
2597
+ el.setAttribute(forProp, 'x');
2598
+ if (el.htmlFor !== 'x') {
2599
+ el.setAttribute('htmlFor', 'x');
2600
+ if (el.htmlFor === 'x') {
2601
+ forProp = 'htmlFor';
2602
+ }
2603
+ }
2604
+ el = null;
2605
+
2606
+ return {
2607
+ read: {
2608
+ names: {
2609
+ 'class': classProp,
2610
+ 'className': classProp,
2611
+ 'for': forProp,
2612
+ 'htmlFor': forProp
2613
+ },
2614
+ values: {
2615
+ _getAttr: function(element, attribute) {
2616
+ return element.getAttribute(attribute);
2617
+ },
2618
+ _getAttr2: function(element, attribute) {
2619
+ return element.getAttribute(attribute, 2);
2620
+ },
2621
+ _getAttrNode: function(element, attribute) {
2622
+ var node = element.getAttributeNode(attribute);
2623
+ return node ? node.value : "";
2624
+ },
2625
+ _getEv: (function(){
2626
+
2627
+ var el = document.createElement('div'), f;
2628
+ el.onclick = Prototype.emptyFunction;
2629
+ var value = el.getAttribute('onclick');
2630
+
2631
+ if (String(value).indexOf('{') > -1) {
2632
+ f = function(element, attribute) {
2633
+ attribute = element.getAttribute(attribute);
2634
+ if (!attribute) return null;
2635
+ attribute = attribute.toString();
2636
+ attribute = attribute.split('{')[1];
2637
+ attribute = attribute.split('}')[0];
2638
+ return attribute.strip();
2639
+ };
2640
+ }
2641
+ else if (value === '') {
2642
+ f = function(element, attribute) {
2643
+ attribute = element.getAttribute(attribute);
2644
+ if (!attribute) return null;
2645
+ return attribute.strip();
2646
+ };
2647
+ }
2648
+ el = null;
2649
+ return f;
2650
+ })(),
2651
+ _flag: function(element, attribute) {
2652
+ return $(element).hasAttribute(attribute) ? attribute : null;
2653
+ },
2654
+ style: function(element) {
2655
+ return element.style.cssText.toLowerCase();
2656
+ },
2657
+ title: function(element) {
2658
+ return element.title;
2659
+ }
2660
+ }
2661
+ }
2662
+ }
2663
+ })();
2664
+
2665
+ Element._attributeTranslations.write = {
2666
+ names: Object.extend({
2667
+ cellpadding: 'cellPadding',
2668
+ cellspacing: 'cellSpacing'
2669
+ }, Element._attributeTranslations.read.names),
2670
+ values: {
2671
+ checked: function(element, value) {
2672
+ element.checked = !!value;
2673
+ },
2674
+
2675
+ style: function(element, value) {
2676
+ element.style.cssText = value ? value : '';
2677
+ }
2678
+ }
2679
+ };
2680
+
2681
+ Element._attributeTranslations.has = {};
2682
+
2683
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2684
+ 'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
2685
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2686
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2687
+ });
2688
+
2689
+ (function(v) {
2690
+ Object.extend(v, {
2691
+ href: v._getAttr2,
2692
+ src: v._getAttr2,
2693
+ type: v._getAttr,
2694
+ action: v._getAttrNode,
2695
+ disabled: v._flag,
2696
+ checked: v._flag,
2697
+ readonly: v._flag,
2698
+ multiple: v._flag,
2699
+ onload: v._getEv,
2700
+ onunload: v._getEv,
2701
+ onclick: v._getEv,
2702
+ ondblclick: v._getEv,
2703
+ onmousedown: v._getEv,
2704
+ onmouseup: v._getEv,
2705
+ onmouseover: v._getEv,
2706
+ onmousemove: v._getEv,
2707
+ onmouseout: v._getEv,
2708
+ onfocus: v._getEv,
2709
+ onblur: v._getEv,
2710
+ onkeypress: v._getEv,
2711
+ onkeydown: v._getEv,
2712
+ onkeyup: v._getEv,
2713
+ onsubmit: v._getEv,
2714
+ onreset: v._getEv,
2715
+ onselect: v._getEv,
2716
+ onchange: v._getEv
2717
+ });
2718
+ })(Element._attributeTranslations.read.values);
2719
+
2720
+ if (Prototype.BrowserFeatures.ElementExtensions) {
2721
+ (function() {
2722
+ function _descendants(element) {
2723
+ var nodes = element.getElementsByTagName('*'), results = [];
2724
+ for (var i = 0, node; node = nodes[i]; i++)
2725
+ if (node.tagName !== "!") // Filter out comment nodes.
2726
+ results.push(node);
2727
+ return results;
2728
+ }
2729
+
2730
+ Element.Methods.down = function(element, expression, index) {
2731
+ element = $(element);
2732
+ if (arguments.length == 1) return element.firstDescendant();
2733
+ return Object.isNumber(expression) ? _descendants(element)[expression] :
2734
+ Element.select(element, expression)[index || 0];
2735
+ }
2736
+ })();
2737
+ }
2738
+
2739
+ }
2740
+
2741
+ else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2742
+ Element.Methods.setOpacity = function(element, value) {
2743
+ element = $(element);
2744
+ element.style.opacity = (value == 1) ? 0.999999 :
2745
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
2746
+ return element;
2747
+ };
2748
+ }
2749
+
2750
+ else if (Prototype.Browser.WebKit) {
2751
+ Element.Methods.setOpacity = function(element, value) {
2752
+ element = $(element);
2753
+ element.style.opacity = (value == 1 || value === '') ? '' :
2754
+ (value < 0.00001) ? 0 : value;
2755
+
2756
+ if (value == 1)
2757
+ if (element.tagName.toUpperCase() == 'IMG' && element.width) {
2758
+ element.width++; element.width--;
2759
+ } else try {
2760
+ var n = document.createTextNode(' ');
2761
+ element.appendChild(n);
2762
+ element.removeChild(n);
2763
+ } catch (e) { }
2764
+
2765
+ return element;
2766
+ };
2767
+ }
2768
+
2769
+ if ('outerHTML' in document.documentElement) {
2770
+ Element.Methods.replace = function(element, content) {
2771
+ element = $(element);
2772
+
2773
+ if (content && content.toElement) content = content.toElement();
2774
+ if (Object.isElement(content)) {
2775
+ element.parentNode.replaceChild(content, element);
2776
+ return element;
2777
+ }
2778
+
2779
+ content = Object.toHTML(content);
2780
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2781
+
2782
+ if (Element._insertionTranslations.tags[tagName]) {
2783
+ var nextSibling = element.next(),
2784
+ fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2785
+ parent.removeChild(element);
2786
+ if (nextSibling)
2787
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2788
+ else
2789
+ fragments.each(function(node) { parent.appendChild(node) });
2790
+ }
2791
+ else element.outerHTML = content.stripScripts();
2792
+
2793
+ content.evalScripts.bind(content).defer();
2794
+ return element;
2795
+ };
2796
+ }
2797
+
2798
+ Element._returnOffset = function(l, t) {
2799
+ var result = [l, t];
2800
+ result.left = l;
2801
+ result.top = t;
2802
+ return result;
2803
+ };
2804
+
2805
+ Element._getContentFromAnonymousElement = function(tagName, html, force) {
2806
+ var div = new Element('div'),
2807
+ t = Element._insertionTranslations.tags[tagName];
2808
+
2809
+ var workaround = false;
2810
+ if (t) workaround = true;
2811
+ else if (force) {
2812
+ workaround = true;
2813
+ t = ['', '', 0];
2814
+ }
2815
+
2816
+ if (workaround) {
2817
+ div.innerHTML = '&nbsp;' + t[0] + html + t[1];
2818
+ div.removeChild(div.firstChild);
2819
+ for (var i = t[2]; i--; ) {
2820
+ div = div.firstChild;
2821
+ }
2822
+ }
2823
+ else {
2824
+ div.innerHTML = html;
2825
+ }
2826
+ return $A(div.childNodes);
2827
+ };
2828
+
2829
+ Element._insertionTranslations = {
2830
+ before: function(element, node) {
2831
+ element.parentNode.insertBefore(node, element);
2832
+ },
2833
+ top: function(element, node) {
2834
+ element.insertBefore(node, element.firstChild);
2835
+ },
2836
+ bottom: function(element, node) {
2837
+ element.appendChild(node);
2838
+ },
2839
+ after: function(element, node) {
2840
+ element.parentNode.insertBefore(node, element.nextSibling);
2841
+ },
2842
+ tags: {
2843
+ TABLE: ['<table>', '</table>', 1],
2844
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
2845
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2846
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2847
+ SELECT: ['<select>', '</select>', 1]
2848
+ }
2849
+ };
2850
+
2851
+ (function() {
2852
+ var tags = Element._insertionTranslations.tags;
2853
+ Object.extend(tags, {
2854
+ THEAD: tags.TBODY,
2855
+ TFOOT: tags.TBODY,
2856
+ TH: tags.TD
2857
+ });
2858
+ })();
2859
+
2860
+ Element.Methods.Simulated = {
2861
+ hasAttribute: function(element, attribute) {
2862
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
2863
+ var node = $(element).getAttributeNode(attribute);
2864
+ return !!(node && node.specified);
2865
+ }
2866
+ };
2867
+
2868
+ Element.Methods.ByTag = { };
2869
+
2870
+ Object.extend(Element, Element.Methods);
2871
+
2872
+ (function(div) {
2873
+
2874
+ if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
2875
+ window.HTMLElement = { };
2876
+ window.HTMLElement.prototype = div['__proto__'];
2877
+ Prototype.BrowserFeatures.ElementExtensions = true;
2878
+ }
2879
+
2880
+ div = null;
2881
+
2882
+ })(document.createElement('div'));
2883
+
2884
+ Element.extend = (function() {
2885
+
2886
+ function checkDeficiency(tagName) {
2887
+ if (typeof window.Element != 'undefined') {
2888
+ var proto = window.Element.prototype;
2889
+ if (proto) {
2890
+ var id = '_' + (Math.random()+'').slice(2),
2891
+ el = document.createElement(tagName);
2892
+ proto[id] = 'x';
2893
+ var isBuggy = (el[id] !== 'x');
2894
+ delete proto[id];
2895
+ el = null;
2896
+ return isBuggy;
2897
+ }
2898
+ }
2899
+ return false;
2900
+ }
2901
+
2902
+ function extendElementWith(element, methods) {
2903
+ for (var property in methods) {
2904
+ var value = methods[property];
2905
+ if (Object.isFunction(value) && !(property in element))
2906
+ element[property] = value.methodize();
2907
+ }
2908
+ }
2909
+
2910
+ var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
2911
+
2912
+ if (Prototype.BrowserFeatures.SpecificElementExtensions) {
2913
+ if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
2914
+ return function(element) {
2915
+ if (element && typeof element._extendedByPrototype == 'undefined') {
2916
+ var t = element.tagName;
2917
+ if (t && (/^(?:object|applet|embed)$/i.test(t))) {
2918
+ extendElementWith(element, Element.Methods);
2919
+ extendElementWith(element, Element.Methods.Simulated);
2920
+ extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
2921
+ }
2922
+ }
2923
+ return element;
2924
+ }
2925
+ }
2926
+ return Prototype.K;
2927
+ }
2928
+
2929
+ var Methods = { }, ByTag = Element.Methods.ByTag;
2930
+
2931
+ var extend = Object.extend(function(element) {
2932
+ if (!element || typeof element._extendedByPrototype != 'undefined' ||
2933
+ element.nodeType != 1 || element == window) return element;
2934
+
2935
+ var methods = Object.clone(Methods),
2936
+ tagName = element.tagName.toUpperCase();
2937
+
2938
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2939
+
2940
+ extendElementWith(element, methods);
2941
+
2942
+ element._extendedByPrototype = Prototype.emptyFunction;
2943
+ return element;
2944
+
2945
+ }, {
2946
+ refresh: function() {
2947
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
2948
+ Object.extend(Methods, Element.Methods);
2949
+ Object.extend(Methods, Element.Methods.Simulated);
2950
+ }
2951
+ }
2952
+ });
2953
+
2954
+ extend.refresh();
2955
+ return extend;
2956
+ })();
2957
+
2958
+ if (document.documentElement.hasAttribute) {
2959
+ Element.hasAttribute = function(element, attribute) {
2960
+ return element.hasAttribute(attribute);
2961
+ };
2962
+ }
2963
+ else {
2964
+ Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
2965
+ }
2966
+
2967
+ Element.addMethods = function(methods) {
2968
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2969
+
2970
+ if (!methods) {
2971
+ Object.extend(Form, Form.Methods);
2972
+ Object.extend(Form.Element, Form.Element.Methods);
2973
+ Object.extend(Element.Methods.ByTag, {
2974
+ "FORM": Object.clone(Form.Methods),
2975
+ "INPUT": Object.clone(Form.Element.Methods),
2976
+ "SELECT": Object.clone(Form.Element.Methods),
2977
+ "TEXTAREA": Object.clone(Form.Element.Methods),
2978
+ "BUTTON": Object.clone(Form.Element.Methods)
2979
+ });
2980
+ }
2981
+
2982
+ if (arguments.length == 2) {
2983
+ var tagName = methods;
2984
+ methods = arguments[1];
2985
+ }
2986
+
2987
+ if (!tagName) Object.extend(Element.Methods, methods || { });
2988
+ else {
2989
+ if (Object.isArray(tagName)) tagName.each(extend);
2990
+ else extend(tagName);
2991
+ }
2992
+
2993
+ function extend(tagName) {
2994
+ tagName = tagName.toUpperCase();
2995
+ if (!Element.Methods.ByTag[tagName])
2996
+ Element.Methods.ByTag[tagName] = { };
2997
+ Object.extend(Element.Methods.ByTag[tagName], methods);
2998
+ }
2999
+
3000
+ function copy(methods, destination, onlyIfAbsent) {
3001
+ onlyIfAbsent = onlyIfAbsent || false;
3002
+ for (var property in methods) {
3003
+ var value = methods[property];
3004
+ if (!Object.isFunction(value)) continue;
3005
+ if (!onlyIfAbsent || !(property in destination))
3006
+ destination[property] = value.methodize();
3007
+ }
3008
+ }
3009
+
3010
+ function findDOMClass(tagName) {
3011
+ var klass;
3012
+ var trans = {
3013
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3014
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3015
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3016
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3017
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3018
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3019
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3020
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3021
+ "FrameSet", "IFRAME": "IFrame"
3022
+ };
3023
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3024
+ if (window[klass]) return window[klass];
3025
+ klass = 'HTML' + tagName + 'Element';
3026
+ if (window[klass]) return window[klass];
3027
+ klass = 'HTML' + tagName.capitalize() + 'Element';
3028
+ if (window[klass]) return window[klass];
3029
+
3030
+ var element = document.createElement(tagName),
3031
+ proto = element['__proto__'] || element.constructor.prototype;
3032
+
3033
+ element = null;
3034
+ return proto;
3035
+ }
3036
+
3037
+ var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
3038
+ Element.prototype;
3039
+
3040
+ if (F.ElementExtensions) {
3041
+ copy(Element.Methods, elementPrototype);
3042
+ copy(Element.Methods.Simulated, elementPrototype, true);
3043
+ }
3044
+
3045
+ if (F.SpecificElementExtensions) {
3046
+ for (var tag in Element.Methods.ByTag) {
3047
+ var klass = findDOMClass(tag);
3048
+ if (Object.isUndefined(klass)) continue;
3049
+ copy(T[tag], klass.prototype);
3050
+ }
3051
+ }
3052
+
3053
+ Object.extend(Element, Element.Methods);
3054
+ delete Element.ByTag;
3055
+
3056
+ if (Element.extend.refresh) Element.extend.refresh();
3057
+ Element.cache = { };
3058
+ };
3059
+
3060
+
3061
+ document.viewport = {
3062
+
3063
+ getDimensions: function() {
3064
+ return { width: this.getWidth(), height: this.getHeight() };
3065
+ },
3066
+
3067
+ getScrollOffsets: function() {
3068
+ return Element._returnOffset(
3069
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
3070
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
3071
+ }
3072
+ };
3073
+
3074
+ (function(viewport) {
3075
+ var B = Prototype.Browser, doc = document, element, property = {};
3076
+
3077
+ function getRootElement() {
3078
+ if (B.WebKit && !doc.evaluate)
3079
+ return document;
3080
+
3081
+ if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
3082
+ return document.body;
3083
+
3084
+ return document.documentElement;
3085
+ }
3086
+
3087
+ function define(D) {
3088
+ if (!element) element = getRootElement();
3089
+
3090
+ property[D] = 'client' + D;
3091
+
3092
+ viewport['get' + D] = function() { return element[property[D]] };
3093
+ return viewport['get' + D]();
3094
+ }
3095
+
3096
+ viewport.getWidth = define.curry('Width');
3097
+
3098
+ viewport.getHeight = define.curry('Height');
3099
+ })(document.viewport);
3100
+
3101
+
3102
+ Element.Storage = {
3103
+ UID: 1
3104
+ };
3105
+
3106
+ Element.addMethods({
3107
+ getStorage: function(element) {
3108
+ if (!(element = $(element))) return;
3109
+
3110
+ var uid;
3111
+ if (element === window) {
3112
+ uid = 0;
3113
+ } else {
3114
+ if (typeof element._prototypeUID === "undefined")
3115
+ element._prototypeUID = Element.Storage.UID++;
3116
+ uid = element._prototypeUID;
3117
+ }
3118
+
3119
+ if (!Element.Storage[uid])
3120
+ Element.Storage[uid] = $H();
3121
+
3122
+ return Element.Storage[uid];
3123
+ },
3124
+
3125
+ store: function(element, key, value) {
3126
+ if (!(element = $(element))) return;
3127
+
3128
+ if (arguments.length === 2) {
3129
+ Element.getStorage(element).update(key);
3130
+ } else {
3131
+ Element.getStorage(element).set(key, value);
3132
+ }
3133
+
3134
+ return element;
3135
+ },
3136
+
3137
+ retrieve: function(element, key, defaultValue) {
3138
+ if (!(element = $(element))) return;
3139
+ var hash = Element.getStorage(element), value = hash.get(key);
3140
+
3141
+ if (Object.isUndefined(value)) {
3142
+ hash.set(key, defaultValue);
3143
+ value = defaultValue;
3144
+ }
3145
+
3146
+ return value;
3147
+ },
3148
+
3149
+ clone: function(element, deep) {
3150
+ if (!(element = $(element))) return;
3151
+ var clone = element.cloneNode(deep);
3152
+ clone._prototypeUID = void 0;
3153
+ if (deep) {
3154
+ var descendants = Element.select(clone, '*'),
3155
+ i = descendants.length;
3156
+ while (i--) {
3157
+ descendants[i]._prototypeUID = void 0;
3158
+ }
3159
+ }
3160
+ return Element.extend(clone);
3161
+ },
3162
+
3163
+ purge: function(element) {
3164
+ if (!(element = $(element))) return;
3165
+ var purgeElement = Element._purgeElement;
3166
+
3167
+ purgeElement(element);
3168
+
3169
+ var descendants = element.getElementsByTagName('*'),
3170
+ i = descendants.length;
3171
+
3172
+ while (i--) purgeElement(descendants[i]);
3173
+
3174
+ return null;
3175
+ }
3176
+ });
3177
+
3178
+ (function() {
3179
+
3180
+ function toDecimal(pctString) {
3181
+ var match = pctString.match(/^(\d+)%?$/i);
3182
+ if (!match) return null;
3183
+ return (Number(match[1]) / 100);
3184
+ }
3185
+
3186
+ function getPixelValue(value, property, context) {
3187
+ var element = null;
3188
+ if (Object.isElement(value)) {
3189
+ element = value;
3190
+ value = element.getStyle(property);
3191
+ }
3192
+
3193
+ if (value === null) {
3194
+ return null;
3195
+ }
3196
+
3197
+ if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
3198
+ return window.parseFloat(value);
3199
+ }
3200
+
3201
+ var isPercentage = value.include('%'), isViewport = (context === document.viewport);
3202
+
3203
+ if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
3204
+ var style = element.style.left, rStyle = element.runtimeStyle.left;
3205
+ element.runtimeStyle.left = element.currentStyle.left;
3206
+ element.style.left = value || 0;
3207
+ value = element.style.pixelLeft;
3208
+ element.style.left = style;
3209
+ element.runtimeStyle.left = rStyle;
3210
+
3211
+ return value;
3212
+ }
3213
+
3214
+ if (element && isPercentage) {
3215
+ context = context || element.parentNode;
3216
+ var decimal = toDecimal(value);
3217
+ var whole = null;
3218
+ var position = element.getStyle('position');
3219
+
3220
+ var isHorizontal = property.include('left') || property.include('right') ||
3221
+ property.include('width');
3222
+
3223
+ var isVertical = property.include('top') || property.include('bottom') ||
3224
+ property.include('height');
3225
+
3226
+ if (context === document.viewport) {
3227
+ if (isHorizontal) {
3228
+ whole = document.viewport.getWidth();
3229
+ } else if (isVertical) {
3230
+ whole = document.viewport.getHeight();
3231
+ }
3232
+ } else {
3233
+ if (isHorizontal) {
3234
+ whole = $(context).measure('width');
3235
+ } else if (isVertical) {
3236
+ whole = $(context).measure('height');
3237
+ }
3238
+ }
3239
+
3240
+ return (whole === null) ? 0 : whole * decimal;
3241
+ }
3242
+
3243
+ return 0;
3244
+ }
3245
+
3246
+ function toCSSPixels(number) {
3247
+ if (Object.isString(number) && number.endsWith('px')) {
3248
+ return number;
3249
+ }
3250
+ return number + 'px';
3251
+ }
3252
+
3253
+ function isDisplayed(element) {
3254
+ var originalElement = element;
3255
+ while (element && element.parentNode) {
3256
+ var display = element.getStyle('display');
3257
+ if (display === 'none') {
3258
+ return false;
3259
+ }
3260
+ element = $(element.parentNode);
3261
+ }
3262
+ return true;
3263
+ }
3264
+
3265
+ var hasLayout = Prototype.K;
3266
+ if ('currentStyle' in document.documentElement) {
3267
+ hasLayout = function(element) {
3268
+ if (!element.currentStyle.hasLayout) {
3269
+ element.style.zoom = 1;
3270
+ }
3271
+ return element;
3272
+ };
3273
+ }
3274
+
3275
+ function cssNameFor(key) {
3276
+ if (key.include('border')) key = key + '-width';
3277
+ return key.camelize();
3278
+ }
3279
+
3280
+ Element.Layout = Class.create(Hash, {
3281
+ initialize: function($super, element, preCompute) {
3282
+ $super();
3283
+ this.element = $(element);
3284
+
3285
+ Element.Layout.PROPERTIES.each( function(property) {
3286
+ this._set(property, null);
3287
+ }, this);
3288
+
3289
+ if (preCompute) {
3290
+ this._preComputing = true;
3291
+ this._begin();
3292
+ Element.Layout.PROPERTIES.each( this._compute, this );
3293
+ this._end();
3294
+ this._preComputing = false;
3295
+ }
3296
+ },
3297
+
3298
+ _set: function(property, value) {
3299
+ return Hash.prototype.set.call(this, property, value);
3300
+ },
3301
+
3302
+ set: function(property, value) {
3303
+ throw "Properties of Element.Layout are read-only.";
3304
+ },
3305
+
3306
+ get: function($super, property) {
3307
+ var value = $super(property);
3308
+ return value === null ? this._compute(property) : value;
3309
+ },
3310
+
3311
+ _begin: function() {
3312
+ if (this._prepared) return;
3313
+
3314
+ var element = this.element;
3315
+ if (isDisplayed(element)) {
3316
+ this._prepared = true;
3317
+ return;
3318
+ }
3319
+
3320
+ var originalStyles = {
3321
+ position: element.style.position || '',
3322
+ width: element.style.width || '',
3323
+ visibility: element.style.visibility || '',
3324
+ display: element.style.display || ''
3325
+ };
3326
+
3327
+ element.store('prototype_original_styles', originalStyles);
3328
+
3329
+ var position = element.getStyle('position'),
3330
+ width = element.getStyle('width');
3331
+
3332
+ if (width === "0px" || width === null) {
3333
+ element.style.display = 'block';
3334
+ width = element.getStyle('width');
3335
+ }
3336
+
3337
+ var context = (position === 'fixed') ? document.viewport :
3338
+ element.parentNode;
3339
+
3340
+ element.setStyle({
3341
+ position: 'absolute',
3342
+ visibility: 'hidden',
3343
+ display: 'block'
3344
+ });
3345
+
3346
+ var positionedWidth = element.getStyle('width');
3347
+
3348
+ var newWidth;
3349
+ if (width && (positionedWidth === width)) {
3350
+ newWidth = getPixelValue(element, 'width', context);
3351
+ } else if (position === 'absolute' || position === 'fixed') {
3352
+ newWidth = getPixelValue(element, 'width', context);
3353
+ } else {
3354
+ var parent = element.parentNode, pLayout = $(parent).getLayout();
3355
+
3356
+ newWidth = pLayout.get('width') -
3357
+ this.get('margin-left') -
3358
+ this.get('border-left') -
3359
+ this.get('padding-left') -
3360
+ this.get('padding-right') -
3361
+ this.get('border-right') -
3362
+ this.get('margin-right');
3363
+ }
3364
+
3365
+ element.setStyle({ width: newWidth + 'px' });
3366
+
3367
+ this._prepared = true;
3368
+ },
3369
+
3370
+ _end: function() {
3371
+ var element = this.element;
3372
+ var originalStyles = element.retrieve('prototype_original_styles');
3373
+ element.store('prototype_original_styles', null);
3374
+ element.setStyle(originalStyles);
3375
+ this._prepared = false;
3376
+ },
3377
+
3378
+ _compute: function(property) {
3379
+ var COMPUTATIONS = Element.Layout.COMPUTATIONS;
3380
+ if (!(property in COMPUTATIONS)) {
3381
+ throw "Property not found.";
3382
+ }
3383
+
3384
+ return this._set(property, COMPUTATIONS[property].call(this, this.element));
3385
+ },
3386
+
3387
+ toObject: function() {
3388
+ var args = $A(arguments);
3389
+ var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3390
+ args.join(' ').split(' ');
3391
+ var obj = {};
3392
+ keys.each( function(key) {
3393
+ if (!Element.Layout.PROPERTIES.include(key)) return;
3394
+ var value = this.get(key);
3395
+ if (value != null) obj[key] = value;
3396
+ }, this);
3397
+ return obj;
3398
+ },
3399
+
3400
+ toHash: function() {
3401
+ var obj = this.toObject.apply(this, arguments);
3402
+ return new Hash(obj);
3403
+ },
3404
+
3405
+ toCSS: function() {
3406
+ var args = $A(arguments);
3407
+ var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3408
+ args.join(' ').split(' ');
3409
+ var css = {};
3410
+
3411
+ keys.each( function(key) {
3412
+ if (!Element.Layout.PROPERTIES.include(key)) return;
3413
+ if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
3414
+
3415
+ var value = this.get(key);
3416
+ if (value != null) css[cssNameFor(key)] = value + 'px';
3417
+ }, this);
3418
+ return css;
3419
+ },
3420
+
3421
+ inspect: function() {
3422
+ return "#<Element.Layout>";
3423
+ }
3424
+ });
3425
+
3426
+ Object.extend(Element.Layout, {
3427
+ PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
3428
+
3429
+ COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
3430
+
3431
+ COMPUTATIONS: {
3432
+ 'height': function(element) {
3433
+ if (!this._preComputing) this._begin();
3434
+
3435
+ var bHeight = this.get('border-box-height');
3436
+ if (bHeight <= 0) {
3437
+ if (!this._preComputing) this._end();
3438
+ return 0;
3439
+ }
3440
+
3441
+ var bTop = this.get('border-top'),
3442
+ bBottom = this.get('border-bottom');
3443
+
3444
+ var pTop = this.get('padding-top'),
3445
+ pBottom = this.get('padding-bottom');
3446
+
3447
+ if (!this._preComputing) this._end();
3448
+
3449
+ return bHeight - bTop - bBottom - pTop - pBottom;
3450
+ },
3451
+
3452
+ 'width': function(element) {
3453
+ if (!this._preComputing) this._begin();
3454
+
3455
+ var bWidth = this.get('border-box-width');
3456
+ if (bWidth <= 0) {
3457
+ if (!this._preComputing) this._end();
3458
+ return 0;
3459
+ }
3460
+
3461
+ var bLeft = this.get('border-left'),
3462
+ bRight = this.get('border-right');
3463
+
3464
+ var pLeft = this.get('padding-left'),
3465
+ pRight = this.get('padding-right');
3466
+
3467
+ if (!this._preComputing) this._end();
3468
+
3469
+ return bWidth - bLeft - bRight - pLeft - pRight;
3470
+ },
3471
+
3472
+ 'padding-box-height': function(element) {
3473
+ var height = this.get('height'),
3474
+ pTop = this.get('padding-top'),
3475
+ pBottom = this.get('padding-bottom');
3476
+
3477
+ return height + pTop + pBottom;
3478
+ },
3479
+
3480
+ 'padding-box-width': function(element) {
3481
+ var width = this.get('width'),
3482
+ pLeft = this.get('padding-left'),
3483
+ pRight = this.get('padding-right');
3484
+
3485
+ return width + pLeft + pRight;
3486
+ },
3487
+
3488
+ 'border-box-height': function(element) {
3489
+ if (!this._preComputing) this._begin();
3490
+ var height = element.offsetHeight;
3491
+ if (!this._preComputing) this._end();
3492
+ return height;
3493
+ },
3494
+
3495
+ 'border-box-width': function(element) {
3496
+ if (!this._preComputing) this._begin();
3497
+ var width = element.offsetWidth;
3498
+ if (!this._preComputing) this._end();
3499
+ return width;
3500
+ },
3501
+
3502
+ 'margin-box-height': function(element) {
3503
+ var bHeight = this.get('border-box-height'),
3504
+ mTop = this.get('margin-top'),
3505
+ mBottom = this.get('margin-bottom');
3506
+
3507
+ if (bHeight <= 0) return 0;
3508
+
3509
+ return bHeight + mTop + mBottom;
3510
+ },
3511
+
3512
+ 'margin-box-width': function(element) {
3513
+ var bWidth = this.get('border-box-width'),
3514
+ mLeft = this.get('margin-left'),
3515
+ mRight = this.get('margin-right');
3516
+
3517
+ if (bWidth <= 0) return 0;
3518
+
3519
+ return bWidth + mLeft + mRight;
3520
+ },
3521
+
3522
+ 'top': function(element) {
3523
+ var offset = element.positionedOffset();
3524
+ return offset.top;
3525
+ },
3526
+
3527
+ 'bottom': function(element) {
3528
+ var offset = element.positionedOffset(),
3529
+ parent = element.getOffsetParent(),
3530
+ pHeight = parent.measure('height');
3531
+
3532
+ var mHeight = this.get('border-box-height');
3533
+
3534
+ return pHeight - mHeight - offset.top;
3535
+ },
3536
+
3537
+ 'left': function(element) {
3538
+ var offset = element.positionedOffset();
3539
+ return offset.left;
3540
+ },
3541
+
3542
+ 'right': function(element) {
3543
+ var offset = element.positionedOffset(),
3544
+ parent = element.getOffsetParent(),
3545
+ pWidth = parent.measure('width');
3546
+
3547
+ var mWidth = this.get('border-box-width');
3548
+
3549
+ return pWidth - mWidth - offset.left;
3550
+ },
3551
+
3552
+ 'padding-top': function(element) {
3553
+ return getPixelValue(element, 'paddingTop');
3554
+ },
3555
+
3556
+ 'padding-bottom': function(element) {
3557
+ return getPixelValue(element, 'paddingBottom');
3558
+ },
3559
+
3560
+ 'padding-left': function(element) {
3561
+ return getPixelValue(element, 'paddingLeft');
3562
+ },
3563
+
3564
+ 'padding-right': function(element) {
3565
+ return getPixelValue(element, 'paddingRight');
3566
+ },
3567
+
3568
+ 'border-top': function(element) {
3569
+ return getPixelValue(element, 'borderTopWidth');
3570
+ },
3571
+
3572
+ 'border-bottom': function(element) {
3573
+ return getPixelValue(element, 'borderBottomWidth');
3574
+ },
3575
+
3576
+ 'border-left': function(element) {
3577
+ return getPixelValue(element, 'borderLeftWidth');
3578
+ },
3579
+
3580
+ 'border-right': function(element) {
3581
+ return getPixelValue(element, 'borderRightWidth');
3582
+ },
3583
+
3584
+ 'margin-top': function(element) {
3585
+ return getPixelValue(element, 'marginTop');
3586
+ },
3587
+
3588
+ 'margin-bottom': function(element) {
3589
+ return getPixelValue(element, 'marginBottom');
3590
+ },
3591
+
3592
+ 'margin-left': function(element) {
3593
+ return getPixelValue(element, 'marginLeft');
3594
+ },
3595
+
3596
+ 'margin-right': function(element) {
3597
+ return getPixelValue(element, 'marginRight');
3598
+ }
3599
+ }
3600
+ });
3601
+
3602
+ if ('getBoundingClientRect' in document.documentElement) {
3603
+ Object.extend(Element.Layout.COMPUTATIONS, {
3604
+ 'right': function(element) {
3605
+ var parent = hasLayout(element.getOffsetParent());
3606
+ var rect = element.getBoundingClientRect(),
3607
+ pRect = parent.getBoundingClientRect();
3608
+
3609
+ return (pRect.right - rect.right).round();
3610
+ },
3611
+
3612
+ 'bottom': function(element) {
3613
+ var parent = hasLayout(element.getOffsetParent());
3614
+ var rect = element.getBoundingClientRect(),
3615
+ pRect = parent.getBoundingClientRect();
3616
+
3617
+ return (pRect.bottom - rect.bottom).round();
3618
+ }
3619
+ });
3620
+ }
3621
+
3622
+ Element.Offset = Class.create({
3623
+ initialize: function(left, top) {
3624
+ this.left = left.round();
3625
+ this.top = top.round();
3626
+
3627
+ this[0] = this.left;
3628
+ this[1] = this.top;
3629
+ },
3630
+
3631
+ relativeTo: function(offset) {
3632
+ return new Element.Offset(
3633
+ this.left - offset.left,
3634
+ this.top - offset.top
3635
+ );
3636
+ },
3637
+
3638
+ inspect: function() {
3639
+ return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
3640
+ },
3641
+
3642
+ toString: function() {
3643
+ return "[#{left}, #{top}]".interpolate(this);
3644
+ },
3645
+
3646
+ toArray: function() {
3647
+ return [this.left, this.top];
3648
+ }
3649
+ });
3650
+
3651
+ function getLayout(element, preCompute) {
3652
+ return new Element.Layout(element, preCompute);
3653
+ }
3654
+
3655
+ function measure(element, property) {
3656
+ return $(element).getLayout().get(property);
3657
+ }
3658
+
3659
+ function getDimensions(element) {
3660
+ element = $(element);
3661
+ var display = Element.getStyle(element, 'display');
3662
+
3663
+ if (display && display !== 'none') {
3664
+ return { width: element.offsetWidth, height: element.offsetHeight };
3665
+ }
3666
+
3667
+ var style = element.style;
3668
+ var originalStyles = {
3669
+ visibility: style.visibility,
3670
+ position: style.position,
3671
+ display: style.display
3672
+ };
3673
+
3674
+ var newStyles = {
3675
+ visibility: 'hidden',
3676
+ display: 'block'
3677
+ };
3678
+
3679
+ if (originalStyles.position !== 'fixed')
3680
+ newStyles.position = 'absolute';
3681
+
3682
+ Element.setStyle(element, newStyles);
3683
+
3684
+ var dimensions = {
3685
+ width: element.offsetWidth,
3686
+ height: element.offsetHeight
3687
+ };
3688
+
3689
+ Element.setStyle(element, originalStyles);
3690
+
3691
+ return dimensions;
3692
+ }
3693
+
3694
+ function getOffsetParent(element) {
3695
+ element = $(element);
3696
+
3697
+ if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3698
+ return $(document.body);
3699
+
3700
+ var isInline = (Element.getStyle(element, 'display') === 'inline');
3701
+ if (!isInline && element.offsetParent) return $(element.offsetParent);
3702
+
3703
+ while ((element = element.parentNode) && element !== document.body) {
3704
+ if (Element.getStyle(element, 'position') !== 'static') {
3705
+ return isHtml(element) ? $(document.body) : $(element);
3706
+ }
3707
+ }
3708
+
3709
+ return $(document.body);
3710
+ }
3711
+
3712
+
3713
+ function cumulativeOffset(element) {
3714
+ element = $(element);
3715
+ var valueT = 0, valueL = 0;
3716
+ if (element.parentNode) {
3717
+ do {
3718
+ valueT += element.offsetTop || 0;
3719
+ valueL += element.offsetLeft || 0;
3720
+ element = element.offsetParent;
3721
+ } while (element);
3722
+ }
3723
+ return new Element.Offset(valueL, valueT);
3724
+ }
3725
+
3726
+ function positionedOffset(element) {
3727
+ element = $(element);
3728
+
3729
+ var layout = element.getLayout();
3730
+
3731
+ var valueT = 0, valueL = 0;
3732
+ do {
3733
+ valueT += element.offsetTop || 0;
3734
+ valueL += element.offsetLeft || 0;
3735
+ element = element.offsetParent;
3736
+ if (element) {
3737
+ if (isBody(element)) break;
3738
+ var p = Element.getStyle(element, 'position');
3739
+ if (p !== 'static') break;
3740
+ }
3741
+ } while (element);
3742
+
3743
+ valueL -= layout.get('margin-top');
3744
+ valueT -= layout.get('margin-left');
3745
+
3746
+ return new Element.Offset(valueL, valueT);
3747
+ }
3748
+
3749
+ function cumulativeScrollOffset(element) {
3750
+ var valueT = 0, valueL = 0;
3751
+ do {
3752
+ valueT += element.scrollTop || 0;
3753
+ valueL += element.scrollLeft || 0;
3754
+ element = element.parentNode;
3755
+ } while (element);
3756
+ return new Element.Offset(valueL, valueT);
3757
+ }
3758
+
3759
+ function viewportOffset(forElement) {
3760
+ element = $(element);
3761
+ var valueT = 0, valueL = 0, docBody = document.body;
3762
+
3763
+ var element = forElement;
3764
+ do {
3765
+ valueT += element.offsetTop || 0;
3766
+ valueL += element.offsetLeft || 0;
3767
+ if (element.offsetParent == docBody &&
3768
+ Element.getStyle(element, 'position') == 'absolute') break;
3769
+ } while (element = element.offsetParent);
3770
+
3771
+ element = forElement;
3772
+ do {
3773
+ if (element != docBody) {
3774
+ valueT -= element.scrollTop || 0;
3775
+ valueL -= element.scrollLeft || 0;
3776
+ }
3777
+ } while (element = element.parentNode);
3778
+ return new Element.Offset(valueL, valueT);
3779
+ }
3780
+
3781
+ function absolutize(element) {
3782
+ element = $(element);
3783
+
3784
+ if (Element.getStyle(element, 'position') === 'absolute') {
3785
+ return element;
3786
+ }
3787
+
3788
+ var offsetParent = getOffsetParent(element);
3789
+ var eOffset = element.viewportOffset(),
3790
+ pOffset = offsetParent.viewportOffset();
3791
+
3792
+ var offset = eOffset.relativeTo(pOffset);
3793
+ var layout = element.getLayout();
3794
+
3795
+ element.store('prototype_absolutize_original_styles', {
3796
+ left: element.getStyle('left'),
3797
+ top: element.getStyle('top'),
3798
+ width: element.getStyle('width'),
3799
+ height: element.getStyle('height')
3800
+ });
3801
+
3802
+ element.setStyle({
3803
+ position: 'absolute',
3804
+ top: offset.top + 'px',
3805
+ left: offset.left + 'px',
3806
+ width: layout.get('width') + 'px',
3807
+ height: layout.get('height') + 'px'
3808
+ });
3809
+
3810
+ return element;
3811
+ }
3812
+
3813
+ function relativize(element) {
3814
+ element = $(element);
3815
+ if (Element.getStyle(element, 'position') === 'relative') {
3816
+ return element;
3817
+ }
3818
+
3819
+ var originalStyles =
3820
+ element.retrieve('prototype_absolutize_original_styles');
3821
+
3822
+ if (originalStyles) element.setStyle(originalStyles);
3823
+ return element;
3824
+ }
3825
+
3826
+ if (Prototype.Browser.IE) {
3827
+ getOffsetParent = getOffsetParent.wrap(
3828
+ function(proceed, element) {
3829
+ element = $(element);
3830
+
3831
+ if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3832
+ return $(document.body);
3833
+
3834
+ var position = element.getStyle('position');
3835
+ if (position !== 'static') return proceed(element);
3836
+
3837
+ element.setStyle({ position: 'relative' });
3838
+ var value = proceed(element);
3839
+ element.setStyle({ position: position });
3840
+ return value;
3841
+ }
3842
+ );
3843
+
3844
+ positionedOffset = positionedOffset.wrap(function(proceed, element) {
3845
+ element = $(element);
3846
+ if (!element.parentNode) return new Element.Offset(0, 0);
3847
+ var position = element.getStyle('position');
3848
+ if (position !== 'static') return proceed(element);
3849
+
3850
+ var offsetParent = element.getOffsetParent();
3851
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
3852
+ hasLayout(offsetParent);
3853
+
3854
+ element.setStyle({ position: 'relative' });
3855
+ var value = proceed(element);
3856
+ element.setStyle({ position: position });
3857
+ return value;
3858
+ });
3859
+ } else if (Prototype.Browser.Webkit) {
3860
+ cumulativeOffset = function(element) {
3861
+ element = $(element);
3862
+ var valueT = 0, valueL = 0;
3863
+ do {
3864
+ valueT += element.offsetTop || 0;
3865
+ valueL += element.offsetLeft || 0;
3866
+ if (element.offsetParent == document.body)
3867
+ if (Element.getStyle(element, 'position') == 'absolute') break;
3868
+
3869
+ element = element.offsetParent;
3870
+ } while (element);
3871
+
3872
+ return new Element.Offset(valueL, valueT);
3873
+ };
3874
+ }
3875
+
3876
+
3877
+ Element.addMethods({
3878
+ getLayout: getLayout,
3879
+ measure: measure,
3880
+ getDimensions: getDimensions,
3881
+ getOffsetParent: getOffsetParent,
3882
+ cumulativeOffset: cumulativeOffset,
3883
+ positionedOffset: positionedOffset,
3884
+ cumulativeScrollOffset: cumulativeScrollOffset,
3885
+ viewportOffset: viewportOffset,
3886
+ absolutize: absolutize,
3887
+ relativize: relativize
3888
+ });
3889
+
3890
+ function isBody(element) {
3891
+ return element.nodeName.toUpperCase() === 'BODY';
3892
+ }
3893
+
3894
+ function isHtml(element) {
3895
+ return element.nodeName.toUpperCase() === 'HTML';
3896
+ }
3897
+
3898
+ function isDocument(element) {
3899
+ return element.nodeType === Node.DOCUMENT_NODE;
3900
+ }
3901
+
3902
+ function isDetached(element) {
3903
+ return element !== document.body &&
3904
+ !Element.descendantOf(element, document.body);
3905
+ }
3906
+
3907
+ if ('getBoundingClientRect' in document.documentElement) {
3908
+ Element.addMethods({
3909
+ viewportOffset: function(element) {
3910
+ element = $(element);
3911
+ if (isDetached(element)) return new Element.Offset(0, 0);
3912
+
3913
+ var rect = element.getBoundingClientRect(),
3914
+ docEl = document.documentElement;
3915
+ return new Element.Offset(rect.left - docEl.clientLeft,
3916
+ rect.top - docEl.clientTop);
3917
+ }
3918
+ });
3919
+ }
3920
+ })();
3921
+ window.$$ = function() {
3922
+ var expression = $A(arguments).join(', ');
3923
+ return Prototype.Selector.select(expression, document);
3924
+ };
3925
+
3926
+ Prototype.Selector = (function() {
3927
+
3928
+ function select() {
3929
+ throw new Error('Method "Prototype.Selector.select" must be defined.');
3930
+ }
3931
+
3932
+ function match() {
3933
+ throw new Error('Method "Prototype.Selector.match" must be defined.');
3934
+ }
3935
+
3936
+ function find(elements, expression, index) {
3937
+ index = index || 0;
3938
+ var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
3939
+
3940
+ for (i = 0; i < length; i++) {
3941
+ if (match(elements[i], expression) && index == matchIndex++) {
3942
+ return Element.extend(elements[i]);
3943
+ }
3944
+ }
3945
+ }
3946
+
3947
+ function extendElements(elements) {
3948
+ for (var i = 0, length = elements.length; i < length; i++) {
3949
+ Element.extend(elements[i]);
3950
+ }
3951
+ return elements;
3952
+ }
3953
+
3954
+
3955
+ var K = Prototype.K;
3956
+
3957
+ return {
3958
+ select: select,
3959
+ match: match,
3960
+ find: find,
3961
+ extendElements: (Element.extend === K) ? K : extendElements,
3962
+ extendElement: Element.extend
3963
+ };
3964
+ })();
3965
+ Prototype._original_property = window.Sizzle;
3966
+ /*!
3967
+ * Sizzle CSS Selector Engine - v1.0
3968
+ * Copyright 2009, The Dojo Foundation
3969
+ * Released under the MIT, BSD, and GPL Licenses.
3970
+ * More information: http://sizzlejs.com/
3971
+ */
3972
+ (function(){
3973
+
3974
+ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
3975
+ done = 0,
3976
+ toString = Object.prototype.toString,
3977
+ hasDuplicate = false,
3978
+ baseHasDuplicate = true;
3979
+
3980
+ [0, 0].sort(function(){
3981
+ baseHasDuplicate = false;
3982
+ return 0;
3983
+ });
3984
+
3985
+ var Sizzle = function(selector, context, results, seed) {
3986
+ results = results || [];
3987
+ var origContext = context = context || document;
3988
+
3989
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
3990
+ return [];
3991
+ }
3992
+
3993
+ if ( !selector || typeof selector !== "string" ) {
3994
+ return results;
3995
+ }
3996
+
3997
+ var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
3998
+ soFar = selector;
3999
+
4000
+ while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
4001
+ soFar = m[3];
4002
+
4003
+ parts.push( m[1] );
4004
+
4005
+ if ( m[2] ) {
4006
+ extra = m[3];
4007
+ break;
4008
+ }
4009
+ }
4010
+
4011
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
4012
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
4013
+ set = posProcess( parts[0] + parts[1], context );
4014
+ } else {
4015
+ set = Expr.relative[ parts[0] ] ?
4016
+ [ context ] :
4017
+ Sizzle( parts.shift(), context );
4018
+
4019
+ while ( parts.length ) {
4020
+ selector = parts.shift();
4021
+
4022
+ if ( Expr.relative[ selector ] )
4023
+ selector += parts.shift();
4024
+
4025
+ set = posProcess( selector, set );
4026
+ }
4027
+ }
4028
+ } else {
4029
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
4030
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
4031
+ var ret = Sizzle.find( parts.shift(), context, contextXML );
4032
+ context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
4033
+ }
4034
+
4035
+ if ( context ) {
4036
+ var ret = seed ?
4037
+ { expr: parts.pop(), set: makeArray(seed) } :
4038
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
4039
+ set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
4040
+
4041
+ if ( parts.length > 0 ) {
4042
+ checkSet = makeArray(set);
4043
+ } else {
4044
+ prune = false;
4045
+ }
4046
+
4047
+ while ( parts.length ) {
4048
+ var cur = parts.pop(), pop = cur;
4049
+
4050
+ if ( !Expr.relative[ cur ] ) {
4051
+ cur = "";
4052
+ } else {
4053
+ pop = parts.pop();
4054
+ }
4055
+
4056
+ if ( pop == null ) {
4057
+ pop = context;
4058
+ }
4059
+
4060
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
4061
+ }
4062
+ } else {
4063
+ checkSet = parts = [];
4064
+ }
4065
+ }
4066
+
4067
+ if ( !checkSet ) {
4068
+ checkSet = set;
4069
+ }
4070
+
4071
+ if ( !checkSet ) {
4072
+ throw "Syntax error, unrecognized expression: " + (cur || selector);
4073
+ }
4074
+
4075
+ if ( toString.call(checkSet) === "[object Array]" ) {
4076
+ if ( !prune ) {
4077
+ results.push.apply( results, checkSet );
4078
+ } else if ( context && context.nodeType === 1 ) {
4079
+ for ( var i = 0; checkSet[i] != null; i++ ) {
4080
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
4081
+ results.push( set[i] );
4082
+ }
4083
+ }
4084
+ } else {
4085
+ for ( var i = 0; checkSet[i] != null; i++ ) {
4086
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
4087
+ results.push( set[i] );
4088
+ }
4089
+ }
4090
+ }
4091
+ } else {
4092
+ makeArray( checkSet, results );
4093
+ }
4094
+
4095
+ if ( extra ) {
4096
+ Sizzle( extra, origContext, results, seed );
4097
+ Sizzle.uniqueSort( results );
4098
+ }
4099
+
4100
+ return results;
4101
+ };
4102
+
4103
+ Sizzle.uniqueSort = function(results){
4104
+ if ( sortOrder ) {
4105
+ hasDuplicate = baseHasDuplicate;
4106
+ results.sort(sortOrder);
4107
+
4108
+ if ( hasDuplicate ) {
4109
+ for ( var i = 1; i < results.length; i++ ) {
4110
+ if ( results[i] === results[i-1] ) {
4111
+ results.splice(i--, 1);
4112
+ }
4113
+ }
4114
+ }
4115
+ }
4116
+
4117
+ return results;
4118
+ };
4119
+
4120
+ Sizzle.matches = function(expr, set){
4121
+ return Sizzle(expr, null, null, set);
4122
+ };
4123
+
4124
+ Sizzle.find = function(expr, context, isXML){
4125
+ var set, match;
4126
+
4127
+ if ( !expr ) {
4128
+ return [];
4129
+ }
4130
+
4131
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
4132
+ var type = Expr.order[i], match;
4133
+
4134
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
4135
+ var left = match[1];
4136
+ match.splice(1,1);
4137
+
4138
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
4139
+ match[1] = (match[1] || "").replace(/\\/g, "");
4140
+ set = Expr.find[ type ]( match, context, isXML );
4141
+ if ( set != null ) {
4142
+ expr = expr.replace( Expr.match[ type ], "" );
4143
+ break;
4144
+ }
4145
+ }
4146
+ }
4147
+ }
4148
+
4149
+ if ( !set ) {
4150
+ set = context.getElementsByTagName("*");
4151
+ }
4152
+
4153
+ return {set: set, expr: expr};
4154
+ };
4155
+
4156
+ Sizzle.filter = function(expr, set, inplace, not){
4157
+ var old = expr, result = [], curLoop = set, match, anyFound,
4158
+ isXMLFilter = set && set[0] && isXML(set[0]);
4159
+
4160
+ while ( expr && set.length ) {
4161
+ for ( var type in Expr.filter ) {
4162
+ if ( (match = Expr.match[ type ].exec( expr )) != null ) {
4163
+ var filter = Expr.filter[ type ], found, item;
4164
+ anyFound = false;
4165
+
4166
+ if ( curLoop == result ) {
4167
+ result = [];
4168
+ }
4169
+
4170
+ if ( Expr.preFilter[ type ] ) {
4171
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
4172
+
4173
+ if ( !match ) {
4174
+ anyFound = found = true;
4175
+ } else if ( match === true ) {
4176
+ continue;
4177
+ }
4178
+ }
4179
+
4180
+ if ( match ) {
4181
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
4182
+ if ( item ) {
4183
+ found = filter( item, match, i, curLoop );
4184
+ var pass = not ^ !!found;
4185
+
4186
+ if ( inplace && found != null ) {
4187
+ if ( pass ) {
4188
+ anyFound = true;
4189
+ } else {
4190
+ curLoop[i] = false;
4191
+ }
4192
+ } else if ( pass ) {
4193
+ result.push( item );
4194
+ anyFound = true;
4195
+ }
4196
+ }
4197
+ }
4198
+ }
4199
+
4200
+ if ( found !== undefined ) {
4201
+ if ( !inplace ) {
4202
+ curLoop = result;
4203
+ }
4204
+
4205
+ expr = expr.replace( Expr.match[ type ], "" );
4206
+
4207
+ if ( !anyFound ) {
4208
+ return [];
4209
+ }
4210
+
4211
+ break;
4212
+ }
4213
+ }
4214
+ }
4215
+
4216
+ if ( expr == old ) {
4217
+ if ( anyFound == null ) {
4218
+ throw "Syntax error, unrecognized expression: " + expr;
4219
+ } else {
4220
+ break;
4221
+ }
4222
+ }
4223
+
4224
+ old = expr;
4225
+ }
4226
+
4227
+ return curLoop;
4228
+ };
4229
+
4230
+ var Expr = Sizzle.selectors = {
4231
+ order: [ "ID", "NAME", "TAG" ],
4232
+ match: {
4233
+ ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4234
+ CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4235
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
4236
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
4237
+ TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
4238
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
4239
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
4240
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
4241
+ },
4242
+ leftMatch: {},
4243
+ attrMap: {
4244
+ "class": "className",
4245
+ "for": "htmlFor"
4246
+ },
4247
+ attrHandle: {
4248
+ href: function(elem){
4249
+ return elem.getAttribute("href");
4250
+ }
4251
+ },
4252
+ relative: {
4253
+ "+": function(checkSet, part, isXML){
4254
+ var isPartStr = typeof part === "string",
4255
+ isTag = isPartStr && !/\W/.test(part),
4256
+ isPartStrNotTag = isPartStr && !isTag;
4257
+
4258
+ if ( isTag && !isXML ) {
4259
+ part = part.toUpperCase();
4260
+ }
4261
+
4262
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
4263
+ if ( (elem = checkSet[i]) ) {
4264
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
4265
+
4266
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
4267
+ elem || false :
4268
+ elem === part;
4269
+ }
4270
+ }
4271
+
4272
+ if ( isPartStrNotTag ) {
4273
+ Sizzle.filter( part, checkSet, true );
4274
+ }
4275
+ },
4276
+ ">": function(checkSet, part, isXML){
4277
+ var isPartStr = typeof part === "string";
4278
+
4279
+ if ( isPartStr && !/\W/.test(part) ) {
4280
+ part = isXML ? part : part.toUpperCase();
4281
+
4282
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4283
+ var elem = checkSet[i];
4284
+ if ( elem ) {
4285
+ var parent = elem.parentNode;
4286
+ checkSet[i] = parent.nodeName === part ? parent : false;
4287
+ }
4288
+ }
4289
+ } else {
4290
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4291
+ var elem = checkSet[i];
4292
+ if ( elem ) {
4293
+ checkSet[i] = isPartStr ?
4294
+ elem.parentNode :
4295
+ elem.parentNode === part;
4296
+ }
4297
+ }
4298
+
4299
+ if ( isPartStr ) {
4300
+ Sizzle.filter( part, checkSet, true );
4301
+ }
4302
+ }
4303
+ },
4304
+ "": function(checkSet, part, isXML){
4305
+ var doneName = done++, checkFn = dirCheck;
4306
+
4307
+ if ( !/\W/.test(part) ) {
4308
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
4309
+ checkFn = dirNodeCheck;
4310
+ }
4311
+
4312
+ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
4313
+ },
4314
+ "~": function(checkSet, part, isXML){
4315
+ var doneName = done++, checkFn = dirCheck;
4316
+
4317
+ if ( typeof part === "string" && !/\W/.test(part) ) {
4318
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
4319
+ checkFn = dirNodeCheck;
4320
+ }
4321
+
4322
+ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
4323
+ }
4324
+ },
4325
+ find: {
4326
+ ID: function(match, context, isXML){
4327
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
4328
+ var m = context.getElementById(match[1]);
4329
+ return m ? [m] : [];
4330
+ }
4331
+ },
4332
+ NAME: function(match, context, isXML){
4333
+ if ( typeof context.getElementsByName !== "undefined" ) {
4334
+ var ret = [], results = context.getElementsByName(match[1]);
4335
+
4336
+ for ( var i = 0, l = results.length; i < l; i++ ) {
4337
+ if ( results[i].getAttribute("name") === match[1] ) {
4338
+ ret.push( results[i] );
4339
+ }
4340
+ }
4341
+
4342
+ return ret.length === 0 ? null : ret;
4343
+ }
4344
+ },
4345
+ TAG: function(match, context){
4346
+ return context.getElementsByTagName(match[1]);
4347
+ }
4348
+ },
4349
+ preFilter: {
4350
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
4351
+ match = " " + match[1].replace(/\\/g, "") + " ";
4352
+
4353
+ if ( isXML ) {
4354
+ return match;
4355
+ }
4356
+
4357
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
4358
+ if ( elem ) {
4359
+ if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
4360
+ if ( !inplace )
4361
+ result.push( elem );
4362
+ } else if ( inplace ) {
4363
+ curLoop[i] = false;
4364
+ }
4365
+ }
4366
+ }
4367
+
4368
+ return false;
4369
+ },
4370
+ ID: function(match){
4371
+ return match[1].replace(/\\/g, "");
4372
+ },
4373
+ TAG: function(match, curLoop){
4374
+ for ( var i = 0; curLoop[i] === false; i++ ){}
4375
+ return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
4376
+ },
4377
+ CHILD: function(match){
4378
+ if ( match[1] == "nth" ) {
4379
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
4380
+ match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
4381
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
4382
+
4383
+ match[2] = (test[1] + (test[2] || 1)) - 0;
4384
+ match[3] = test[3] - 0;
4385
+ }
4386
+
4387
+ match[0] = done++;
4388
+
4389
+ return match;
4390
+ },
4391
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
4392
+ var name = match[1].replace(/\\/g, "");
4393
+
4394
+ if ( !isXML && Expr.attrMap[name] ) {
4395
+ match[1] = Expr.attrMap[name];
4396
+ }
4397
+
4398
+ if ( match[2] === "~=" ) {
4399
+ match[4] = " " + match[4] + " ";
4400
+ }
4401
+
4402
+ return match;
4403
+ },
4404
+ PSEUDO: function(match, curLoop, inplace, result, not){
4405
+ if ( match[1] === "not" ) {
4406
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
4407
+ match[3] = Sizzle(match[3], null, null, curLoop);
4408
+ } else {
4409
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
4410
+ if ( !inplace ) {
4411
+ result.push.apply( result, ret );
4412
+ }
4413
+ return false;
4414
+ }
4415
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
4416
+ return true;
4417
+ }
4418
+
4419
+ return match;
4420
+ },
4421
+ POS: function(match){
4422
+ match.unshift( true );
4423
+ return match;
4424
+ }
4425
+ },
4426
+ filters: {
4427
+ enabled: function(elem){
4428
+ return elem.disabled === false && elem.type !== "hidden";
4429
+ },
4430
+ disabled: function(elem){
4431
+ return elem.disabled === true;
4432
+ },
4433
+ checked: function(elem){
4434
+ return elem.checked === true;
4435
+ },
4436
+ selected: function(elem){
4437
+ elem.parentNode.selectedIndex;
4438
+ return elem.selected === true;
4439
+ },
4440
+ parent: function(elem){
4441
+ return !!elem.firstChild;
4442
+ },
4443
+ empty: function(elem){
4444
+ return !elem.firstChild;
4445
+ },
4446
+ has: function(elem, i, match){
4447
+ return !!Sizzle( match[3], elem ).length;
4448
+ },
4449
+ header: function(elem){
4450
+ return /h\d/i.test( elem.nodeName );
4451
+ },
4452
+ text: function(elem){
4453
+ return "text" === elem.type;
4454
+ },
4455
+ radio: function(elem){
4456
+ return "radio" === elem.type;
4457
+ },
4458
+ checkbox: function(elem){
4459
+ return "checkbox" === elem.type;
4460
+ },
4461
+ file: function(elem){
4462
+ return "file" === elem.type;
4463
+ },
4464
+ password: function(elem){
4465
+ return "password" === elem.type;
4466
+ },
4467
+ submit: function(elem){
4468
+ return "submit" === elem.type;
4469
+ },
4470
+ image: function(elem){
4471
+ return "image" === elem.type;
4472
+ },
4473
+ reset: function(elem){
4474
+ return "reset" === elem.type;
4475
+ },
4476
+ button: function(elem){
4477
+ return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
4478
+ },
4479
+ input: function(elem){
4480
+ return /input|select|textarea|button/i.test(elem.nodeName);
4481
+ }
4482
+ },
4483
+ setFilters: {
4484
+ first: function(elem, i){
4485
+ return i === 0;
4486
+ },
4487
+ last: function(elem, i, match, array){
4488
+ return i === array.length - 1;
4489
+ },
4490
+ even: function(elem, i){
4491
+ return i % 2 === 0;
4492
+ },
4493
+ odd: function(elem, i){
4494
+ return i % 2 === 1;
4495
+ },
4496
+ lt: function(elem, i, match){
4497
+ return i < match[3] - 0;
4498
+ },
4499
+ gt: function(elem, i, match){
4500
+ return i > match[3] - 0;
4501
+ },
4502
+ nth: function(elem, i, match){
4503
+ return match[3] - 0 == i;
4504
+ },
4505
+ eq: function(elem, i, match){
4506
+ return match[3] - 0 == i;
4507
+ }
4508
+ },
4509
+ filter: {
4510
+ PSEUDO: function(elem, match, i, array){
4511
+ var name = match[1], filter = Expr.filters[ name ];
4512
+
4513
+ if ( filter ) {
4514
+ return filter( elem, i, match, array );
4515
+ } else if ( name === "contains" ) {
4516
+ return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
4517
+ } else if ( name === "not" ) {
4518
+ var not = match[3];
4519
+
4520
+ for ( var i = 0, l = not.length; i < l; i++ ) {
4521
+ if ( not[i] === elem ) {
4522
+ return false;
4523
+ }
4524
+ }
4525
+
4526
+ return true;
4527
+ }
4528
+ },
4529
+ CHILD: function(elem, match){
4530
+ var type = match[1], node = elem;
4531
+ switch (type) {
4532
+ case 'only':
4533
+ case 'first':
4534
+ while ( (node = node.previousSibling) ) {
4535
+ if ( node.nodeType === 1 ) return false;
4536
+ }
4537
+ if ( type == 'first') return true;
4538
+ node = elem;
4539
+ case 'last':
4540
+ while ( (node = node.nextSibling) ) {
4541
+ if ( node.nodeType === 1 ) return false;
4542
+ }
4543
+ return true;
4544
+ case 'nth':
4545
+ var first = match[2], last = match[3];
4546
+
4547
+ if ( first == 1 && last == 0 ) {
4548
+ return true;
4549
+ }
4550
+
4551
+ var doneName = match[0],
4552
+ parent = elem.parentNode;
4553
+
4554
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
4555
+ var count = 0;
4556
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
4557
+ if ( node.nodeType === 1 ) {
4558
+ node.nodeIndex = ++count;
4559
+ }
4560
+ }
4561
+ parent.sizcache = doneName;
4562
+ }
4563
+
4564
+ var diff = elem.nodeIndex - last;
4565
+ if ( first == 0 ) {
4566
+ return diff == 0;
4567
+ } else {
4568
+ return ( diff % first == 0 && diff / first >= 0 );
4569
+ }
4570
+ }
4571
+ },
4572
+ ID: function(elem, match){
4573
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
4574
+ },
4575
+ TAG: function(elem, match){
4576
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
4577
+ },
4578
+ CLASS: function(elem, match){
4579
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
4580
+ .indexOf( match ) > -1;
4581
+ },
4582
+ ATTR: function(elem, match){
4583
+ var name = match[1],
4584
+ result = Expr.attrHandle[ name ] ?
4585
+ Expr.attrHandle[ name ]( elem ) :
4586
+ elem[ name ] != null ?
4587
+ elem[ name ] :
4588
+ elem.getAttribute( name ),
4589
+ value = result + "",
4590
+ type = match[2],
4591
+ check = match[4];
4592
+
4593
+ return result == null ?
4594
+ type === "!=" :
4595
+ type === "=" ?
4596
+ value === check :
4597
+ type === "*=" ?
4598
+ value.indexOf(check) >= 0 :
4599
+ type === "~=" ?
4600
+ (" " + value + " ").indexOf(check) >= 0 :
4601
+ !check ?
4602
+ value && result !== false :
4603
+ type === "!=" ?
4604
+ value != check :
4605
+ type === "^=" ?
4606
+ value.indexOf(check) === 0 :
4607
+ type === "$=" ?
4608
+ value.substr(value.length - check.length) === check :
4609
+ type === "|=" ?
4610
+ value === check || value.substr(0, check.length + 1) === check + "-" :
4611
+ false;
4612
+ },
4613
+ POS: function(elem, match, i, array){
4614
+ var name = match[2], filter = Expr.setFilters[ name ];
4615
+
4616
+ if ( filter ) {
4617
+ return filter( elem, i, match, array );
4618
+ }
4619
+ }
4620
+ }
4621
+ };
4622
+
4623
+ var origPOS = Expr.match.POS;
4624
+
4625
+ for ( var type in Expr.match ) {
4626
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
4627
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
4628
+ }
4629
+
4630
+ var makeArray = function(array, results) {
4631
+ array = Array.prototype.slice.call( array, 0 );
4632
+
4633
+ if ( results ) {
4634
+ results.push.apply( results, array );
4635
+ return results;
4636
+ }
4637
+
4638
+ return array;
4639
+ };
4640
+
4641
+ try {
4642
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 );
4643
+
4644
+ } catch(e){
4645
+ makeArray = function(array, results) {
4646
+ var ret = results || [];
4647
+
4648
+ if ( toString.call(array) === "[object Array]" ) {
4649
+ Array.prototype.push.apply( ret, array );
4650
+ } else {
4651
+ if ( typeof array.length === "number" ) {
4652
+ for ( var i = 0, l = array.length; i < l; i++ ) {
4653
+ ret.push( array[i] );
4654
+ }
4655
+ } else {
4656
+ for ( var i = 0; array[i]; i++ ) {
4657
+ ret.push( array[i] );
4658
+ }
4659
+ }
4660
+ }
4661
+
4662
+ return ret;
4663
+ };
4664
+ }
4665
+
4666
+ var sortOrder;
4667
+
4668
+ if ( document.documentElement.compareDocumentPosition ) {
4669
+ sortOrder = function( a, b ) {
4670
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
4671
+ if ( a == b ) {
4672
+ hasDuplicate = true;
4673
+ }
4674
+ return 0;
4675
+ }
4676
+
4677
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
4678
+ if ( ret === 0 ) {
4679
+ hasDuplicate = true;
4680
+ }
4681
+ return ret;
4682
+ };
4683
+ } else if ( "sourceIndex" in document.documentElement ) {
4684
+ sortOrder = function( a, b ) {
4685
+ if ( !a.sourceIndex || !b.sourceIndex ) {
4686
+ if ( a == b ) {
4687
+ hasDuplicate = true;
4688
+ }
4689
+ return 0;
4690
+ }
4691
+
4692
+ var ret = a.sourceIndex - b.sourceIndex;
4693
+ if ( ret === 0 ) {
4694
+ hasDuplicate = true;
4695
+ }
4696
+ return ret;
4697
+ };
4698
+ } else if ( document.createRange ) {
4699
+ sortOrder = function( a, b ) {
4700
+ if ( !a.ownerDocument || !b.ownerDocument ) {
4701
+ if ( a == b ) {
4702
+ hasDuplicate = true;
4703
+ }
4704
+ return 0;
4705
+ }
4706
+
4707
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
4708
+ aRange.setStart(a, 0);
4709
+ aRange.setEnd(a, 0);
4710
+ bRange.setStart(b, 0);
4711
+ bRange.setEnd(b, 0);
4712
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
4713
+ if ( ret === 0 ) {
4714
+ hasDuplicate = true;
4715
+ }
4716
+ return ret;
4717
+ };
4718
+ }
4719
+
4720
+ (function(){
4721
+ var form = document.createElement("div"),
4722
+ id = "script" + (new Date).getTime();
4723
+ form.innerHTML = "<a name='" + id + "'/>";
4724
+
4725
+ var root = document.documentElement;
4726
+ root.insertBefore( form, root.firstChild );
4727
+
4728
+ if ( !!document.getElementById( id ) ) {
4729
+ Expr.find.ID = function(match, context, isXML){
4730
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
4731
+ var m = context.getElementById(match[1]);
4732
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
4733
+ }
4734
+ };
4735
+
4736
+ Expr.filter.ID = function(elem, match){
4737
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
4738
+ return elem.nodeType === 1 && node && node.nodeValue === match;
4739
+ };
4740
+ }
4741
+
4742
+ root.removeChild( form );
4743
+ root = form = null; // release memory in IE
4744
+ })();
4745
+
4746
+ (function(){
4747
+
4748
+ var div = document.createElement("div");
4749
+ div.appendChild( document.createComment("") );
4750
+
4751
+ if ( div.getElementsByTagName("*").length > 0 ) {
4752
+ Expr.find.TAG = function(match, context){
4753
+ var results = context.getElementsByTagName(match[1]);
4754
+
4755
+ if ( match[1] === "*" ) {
4756
+ var tmp = [];
4757
+
4758
+ for ( var i = 0; results[i]; i++ ) {
4759
+ if ( results[i].nodeType === 1 ) {
4760
+ tmp.push( results[i] );
4761
+ }
4762
+ }
4763
+
4764
+ results = tmp;
4765
+ }
4766
+
4767
+ return results;
4768
+ };
4769
+ }
4770
+
4771
+ div.innerHTML = "<a href='#'></a>";
4772
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
4773
+ div.firstChild.getAttribute("href") !== "#" ) {
4774
+ Expr.attrHandle.href = function(elem){
4775
+ return elem.getAttribute("href", 2);
4776
+ };
4777
+ }
4778
+
4779
+ div = null; // release memory in IE
4780
+ })();
4781
+
4782
+ if ( document.querySelectorAll ) (function(){
4783
+ var oldSizzle = Sizzle, div = document.createElement("div");
4784
+ div.innerHTML = "<p class='TEST'></p>";
4785
+
4786
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
4787
+ return;
4788
+ }
4789
+
4790
+ Sizzle = function(query, context, extra, seed){
4791
+ context = context || document;
4792
+
4793
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
4794
+ try {
4795
+ return makeArray( context.querySelectorAll(query), extra );
4796
+ } catch(e){}
4797
+ }
4798
+
4799
+ return oldSizzle(query, context, extra, seed);
4800
+ };
4801
+
4802
+ for ( var prop in oldSizzle ) {
4803
+ Sizzle[ prop ] = oldSizzle[ prop ];
4804
+ }
4805
+
4806
+ div = null; // release memory in IE
4807
+ })();
4808
+
4809
+ if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
4810
+ var div = document.createElement("div");
4811
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
4812
+
4813
+ if ( div.getElementsByClassName("e").length === 0 )
4814
+ return;
4815
+
4816
+ div.lastChild.className = "e";
4817
+
4818
+ if ( div.getElementsByClassName("e").length === 1 )
4819
+ return;
4820
+
4821
+ Expr.order.splice(1, 0, "CLASS");
4822
+ Expr.find.CLASS = function(match, context, isXML) {
4823
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
4824
+ return context.getElementsByClassName(match[1]);
4825
+ }
4826
+ };
4827
+
4828
+ div = null; // release memory in IE
4829
+ })();
4830
+
4831
+ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4832
+ var sibDir = dir == "previousSibling" && !isXML;
4833
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4834
+ var elem = checkSet[i];
4835
+ if ( elem ) {
4836
+ if ( sibDir && elem.nodeType === 1 ){
4837
+ elem.sizcache = doneName;
4838
+ elem.sizset = i;
4839
+ }
4840
+ elem = elem[dir];
4841
+ var match = false;
4842
+
4843
+ while ( elem ) {
4844
+ if ( elem.sizcache === doneName ) {
4845
+ match = checkSet[elem.sizset];
4846
+ break;
4847
+ }
4848
+
4849
+ if ( elem.nodeType === 1 && !isXML ){
4850
+ elem.sizcache = doneName;
4851
+ elem.sizset = i;
4852
+ }
4853
+
4854
+ if ( elem.nodeName === cur ) {
4855
+ match = elem;
4856
+ break;
4857
+ }
4858
+
4859
+ elem = elem[dir];
4860
+ }
4861
+
4862
+ checkSet[i] = match;
4863
+ }
4864
+ }
4865
+ }
4866
+
4867
+ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4868
+ var sibDir = dir == "previousSibling" && !isXML;
4869
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4870
+ var elem = checkSet[i];
4871
+ if ( elem ) {
4872
+ if ( sibDir && elem.nodeType === 1 ) {
4873
+ elem.sizcache = doneName;
4874
+ elem.sizset = i;
4875
+ }
4876
+ elem = elem[dir];
4877
+ var match = false;
4878
+
4879
+ while ( elem ) {
4880
+ if ( elem.sizcache === doneName ) {
4881
+ match = checkSet[elem.sizset];
4882
+ break;
4883
+ }
4884
+
4885
+ if ( elem.nodeType === 1 ) {
4886
+ if ( !isXML ) {
4887
+ elem.sizcache = doneName;
4888
+ elem.sizset = i;
4889
+ }
4890
+ if ( typeof cur !== "string" ) {
4891
+ if ( elem === cur ) {
4892
+ match = true;
4893
+ break;
4894
+ }
4895
+
4896
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
4897
+ match = elem;
4898
+ break;
4899
+ }
4900
+ }
4901
+
4902
+ elem = elem[dir];
4903
+ }
4904
+
4905
+ checkSet[i] = match;
4906
+ }
4907
+ }
4908
+ }
4909
+
4910
+ var contains = document.compareDocumentPosition ? function(a, b){
4911
+ return a.compareDocumentPosition(b) & 16;
4912
+ } : function(a, b){
4913
+ return a !== b && (a.contains ? a.contains(b) : true);
4914
+ };
4915
+
4916
+ var isXML = function(elem){
4917
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
4918
+ !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
4919
+ };
4920
+
4921
+ var posProcess = function(selector, context){
4922
+ var tmpSet = [], later = "", match,
4923
+ root = context.nodeType ? [context] : context;
4924
+
4925
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
4926
+ later += match[0];
4927
+ selector = selector.replace( Expr.match.PSEUDO, "" );
4928
+ }
4929
+
4930
+ selector = Expr.relative[selector] ? selector + "*" : selector;
4931
+
4932
+ for ( var i = 0, l = root.length; i < l; i++ ) {
4933
+ Sizzle( selector, root[i], tmpSet );
4934
+ }
4935
+
4936
+ return Sizzle.filter( later, tmpSet );
4937
+ };
4938
+
4939
+
4940
+ window.Sizzle = Sizzle;
4941
+
4942
+ })();
4943
+
4944
+ ;(function(engine) {
4945
+ var extendElements = Prototype.Selector.extendElements;
4946
+
4947
+ function select(selector, scope) {
4948
+ return extendElements(engine(selector, scope || document));
4949
+ }
4950
+
4951
+ function match(element, selector) {
4952
+ return engine.matches(selector, [element]).length == 1;
4953
+ }
4954
+
4955
+ Prototype.Selector.engine = engine;
4956
+ Prototype.Selector.select = select;
4957
+ Prototype.Selector.match = match;
4958
+ })(Sizzle);
4959
+
4960
+ window.Sizzle = Prototype._original_property;
4961
+ delete Prototype._original_property;
4962
+
4963
+ var Form = {
4964
+ reset: function(form) {
4965
+ form = $(form);
4966
+ form.reset();
4967
+ return form;
4968
+ },
4969
+
4970
+ serializeElements: function(elements, options) {
4971
+ if (typeof options != 'object') options = { hash: !!options };
4972
+ else if (Object.isUndefined(options.hash)) options.hash = true;
4973
+ var key, value, submitted = false, submit = options.submit, accumulator, initial;
4974
+
4975
+ if (options.hash) {
4976
+ initial = {};
4977
+ accumulator = function(result, key, value) {
4978
+ if (key in result) {
4979
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
4980
+ result[key].push(value);
4981
+ } else result[key] = value;
4982
+ return result;
4983
+ };
4984
+ } else {
4985
+ initial = '';
4986
+ accumulator = function(result, key, value) {
4987
+ return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value);
4988
+ }
4989
+ }
4990
+
4991
+ return elements.inject(initial, function(result, element) {
4992
+ if (!element.disabled && element.name) {
4993
+ key = element.name; value = $(element).getValue();
4994
+ if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
4995
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
4996
+ result = accumulator(result, key, value);
4997
+ }
4998
+ }
4999
+ return result;
5000
+ });
5001
+ }
5002
+ };
5003
+
5004
+ Form.Methods = {
5005
+ serialize: function(form, options) {
5006
+ return Form.serializeElements(Form.getElements(form), options);
5007
+ },
5008
+
5009
+ getElements: function(form) {
5010
+ var elements = $(form).getElementsByTagName('*'),
5011
+ element,
5012
+ arr = [ ],
5013
+ serializers = Form.Element.Serializers;
5014
+ for (var i = 0; element = elements[i]; i++) {
5015
+ arr.push(element);
5016
+ }
5017
+ return arr.inject([], function(elements, child) {
5018
+ if (serializers[child.tagName.toLowerCase()])
5019
+ elements.push(Element.extend(child));
5020
+ return elements;
5021
+ })
5022
+ },
5023
+
5024
+ getInputs: function(form, typeName, name) {
5025
+ form = $(form);
5026
+ var inputs = form.getElementsByTagName('input');
5027
+
5028
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
5029
+
5030
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
5031
+ var input = inputs[i];
5032
+ if ((typeName && input.type != typeName) || (name && input.name != name))
5033
+ continue;
5034
+ matchingInputs.push(Element.extend(input));
5035
+ }
5036
+
5037
+ return matchingInputs;
5038
+ },
5039
+
5040
+ disable: function(form) {
5041
+ form = $(form);
5042
+ Form.getElements(form).invoke('disable');
5043
+ return form;
5044
+ },
5045
+
5046
+ enable: function(form) {
5047
+ form = $(form);
5048
+ Form.getElements(form).invoke('enable');
5049
+ return form;
5050
+ },
5051
+
5052
+ findFirstElement: function(form) {
5053
+ var elements = $(form).getElements().findAll(function(element) {
5054
+ return 'hidden' != element.type && !element.disabled;
5055
+ });
5056
+ var firstByIndex = elements.findAll(function(element) {
5057
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
5058
+ }).sortBy(function(element) { return element.tabIndex }).first();
5059
+
5060
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
5061
+ return /^(?:input|select|textarea)$/i.test(element.tagName);
5062
+ });
5063
+ },
5064
+
5065
+ focusFirstElement: function(form) {
5066
+ form = $(form);
5067
+ var element = form.findFirstElement();
5068
+ if (element) element.activate();
5069
+ return form;
5070
+ },
5071
+
5072
+ request: function(form, options) {
5073
+ form = $(form), options = Object.clone(options || { });
5074
+
5075
+ var params = options.parameters, action = form.readAttribute('action') || '';
5076
+ if (action.blank()) action = window.location.href;
5077
+ options.parameters = form.serialize(true);
5078
+
5079
+ if (params) {
5080
+ if (Object.isString(params)) params = params.toQueryParams();
5081
+ Object.extend(options.parameters, params);
5082
+ }
5083
+
5084
+ if (form.hasAttribute('method') && !options.method)
5085
+ options.method = form.method;
5086
+
5087
+ return new Ajax.Request(action, options);
5088
+ }
5089
+ };
5090
+
5091
+ /*--------------------------------------------------------------------------*/
5092
+
5093
+
5094
+ Form.Element = {
5095
+ focus: function(element) {
5096
+ $(element).focus();
5097
+ return element;
5098
+ },
5099
+
5100
+ select: function(element) {
5101
+ $(element).select();
5102
+ return element;
5103
+ }
5104
+ };
5105
+
5106
+ Form.Element.Methods = {
5107
+
5108
+ serialize: function(element) {
5109
+ element = $(element);
5110
+ if (!element.disabled && element.name) {
5111
+ var value = element.getValue();
5112
+ if (value != undefined) {
5113
+ var pair = { };
5114
+ pair[element.name] = value;
5115
+ return Object.toQueryString(pair);
5116
+ }
5117
+ }
5118
+ return '';
5119
+ },
5120
+
5121
+ getValue: function(element) {
5122
+ element = $(element);
5123
+ var method = element.tagName.toLowerCase();
5124
+ return Form.Element.Serializers[method](element);
5125
+ },
5126
+
5127
+ setValue: function(element, value) {
5128
+ element = $(element);
5129
+ var method = element.tagName.toLowerCase();
5130
+ Form.Element.Serializers[method](element, value);
5131
+ return element;
5132
+ },
5133
+
5134
+ clear: function(element) {
5135
+ $(element).value = '';
5136
+ return element;
5137
+ },
5138
+
5139
+ present: function(element) {
5140
+ return $(element).value != '';
5141
+ },
5142
+
5143
+ activate: function(element) {
5144
+ element = $(element);
5145
+ try {
5146
+ element.focus();
5147
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
5148
+ !(/^(?:button|reset|submit)$/i.test(element.type))))
5149
+ element.select();
5150
+ } catch (e) { }
5151
+ return element;
5152
+ },
5153
+
5154
+ disable: function(element) {
5155
+ element = $(element);
5156
+ element.disabled = true;
5157
+ return element;
5158
+ },
5159
+
5160
+ enable: function(element) {
5161
+ element = $(element);
5162
+ element.disabled = false;
5163
+ return element;
5164
+ }
5165
+ };
5166
+
5167
+ /*--------------------------------------------------------------------------*/
5168
+
5169
+ var Field = Form.Element;
5170
+
5171
+ var $F = Form.Element.Methods.getValue;
5172
+
5173
+ /*--------------------------------------------------------------------------*/
5174
+
5175
+ Form.Element.Serializers = (function() {
5176
+ function input(element, value) {
5177
+ switch (element.type.toLowerCase()) {
5178
+ case 'checkbox':
5179
+ case 'radio':
5180
+ return inputSelector(element, value);
5181
+ default:
5182
+ return valueSelector(element, value);
5183
+ }
5184
+ }
5185
+
5186
+ function inputSelector(element, value) {
5187
+ if (Object.isUndefined(value))
5188
+ return element.checked ? element.value : null;
5189
+ else element.checked = !!value;
5190
+ }
5191
+
5192
+ function valueSelector(element, value) {
5193
+ if (Object.isUndefined(value)) return element.value;
5194
+ else element.value = value;
5195
+ }
5196
+
5197
+ function select(element, value) {
5198
+ if (Object.isUndefined(value))
5199
+ return (element.type === 'select-one' ? selectOne : selectMany)(element);
5200
+
5201
+ var opt, currentValue, single = !Object.isArray(value);
5202
+ for (var i = 0, length = element.length; i < length; i++) {
5203
+ opt = element.options[i];
5204
+ currentValue = this.optionValue(opt);
5205
+ if (single) {
5206
+ if (currentValue == value) {
5207
+ opt.selected = true;
5208
+ return;
5209
+ }
5210
+ }
5211
+ else opt.selected = value.include(currentValue);
5212
+ }
5213
+ }
5214
+
5215
+ function selectOne(element) {
5216
+ var index = element.selectedIndex;
5217
+ return index >= 0 ? optionValue(element.options[index]) : null;
5218
+ }
5219
+
5220
+ function selectMany(element) {
5221
+ var values, length = element.length;
5222
+ if (!length) return null;
5223
+
5224
+ for (var i = 0, values = []; i < length; i++) {
5225
+ var opt = element.options[i];
5226
+ if (opt.selected) values.push(optionValue(opt));
5227
+ }
5228
+ return values;
5229
+ }
5230
+
5231
+ function optionValue(opt) {
5232
+ return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
5233
+ }
5234
+
5235
+ return {
5236
+ input: input,
5237
+ inputSelector: inputSelector,
5238
+ textarea: valueSelector,
5239
+ select: select,
5240
+ selectOne: selectOne,
5241
+ selectMany: selectMany,
5242
+ optionValue: optionValue,
5243
+ button: valueSelector
5244
+ };
5245
+ })();
5246
+
5247
+ /*--------------------------------------------------------------------------*/
5248
+
5249
+
5250
+ Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
5251
+ initialize: function($super, element, frequency, callback) {
5252
+ $super(callback, frequency);
5253
+ this.element = $(element);
5254
+ this.lastValue = this.getValue();
5255
+ },
5256
+
5257
+ execute: function() {
5258
+ var value = this.getValue();
5259
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
5260
+ this.lastValue != value : String(this.lastValue) != String(value)) {
5261
+ this.callback(this.element, value);
5262
+ this.lastValue = value;
5263
+ }
5264
+ }
5265
+ });
5266
+
5267
+ Form.Element.Observer = Class.create(Abstract.TimedObserver, {
5268
+ getValue: function() {
5269
+ return Form.Element.getValue(this.element);
5270
+ }
5271
+ });
5272
+
5273
+ Form.Observer = Class.create(Abstract.TimedObserver, {
5274
+ getValue: function() {
5275
+ return Form.serialize(this.element);
5276
+ }
5277
+ });
5278
+
5279
+ /*--------------------------------------------------------------------------*/
5280
+
5281
+ Abstract.EventObserver = Class.create({
5282
+ initialize: function(element, callback) {
5283
+ this.element = $(element);
5284
+ this.callback = callback;
5285
+
5286
+ this.lastValue = this.getValue();
5287
+ if (this.element.tagName.toLowerCase() == 'form')
5288
+ this.registerFormCallbacks();
5289
+ else
5290
+ this.registerCallback(this.element);
5291
+ },
5292
+
5293
+ onElementEvent: function() {
5294
+ var value = this.getValue();
5295
+ if (this.lastValue != value) {
5296
+ this.callback(this.element, value);
5297
+ this.lastValue = value;
5298
+ }
5299
+ },
5300
+
5301
+ registerFormCallbacks: function() {
5302
+ Form.getElements(this.element).each(this.registerCallback, this);
5303
+ },
5304
+
5305
+ registerCallback: function(element) {
5306
+ if (element.type) {
5307
+ switch (element.type.toLowerCase()) {
5308
+ case 'checkbox':
5309
+ case 'radio':
5310
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
5311
+ break;
5312
+ default:
5313
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
5314
+ break;
5315
+ }
5316
+ }
5317
+ }
5318
+ });
5319
+
5320
+ Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
5321
+ getValue: function() {
5322
+ return Form.Element.getValue(this.element);
5323
+ }
5324
+ });
5325
+
5326
+ Form.EventObserver = Class.create(Abstract.EventObserver, {
5327
+ getValue: function() {
5328
+ return Form.serialize(this.element);
5329
+ }
5330
+ });
5331
+ (function() {
5332
+
5333
+ var Event = {
5334
+ KEY_BACKSPACE: 8,
5335
+ KEY_TAB: 9,
5336
+ KEY_RETURN: 13,
5337
+ KEY_ESC: 27,
5338
+ KEY_LEFT: 37,
5339
+ KEY_UP: 38,
5340
+ KEY_RIGHT: 39,
5341
+ KEY_DOWN: 40,
5342
+ KEY_DELETE: 46,
5343
+ KEY_HOME: 36,
5344
+ KEY_END: 35,
5345
+ KEY_PAGEUP: 33,
5346
+ KEY_PAGEDOWN: 34,
5347
+ KEY_INSERT: 45,
5348
+
5349
+ cache: {}
5350
+ };
5351
+
5352
+ var docEl = document.documentElement;
5353
+ var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
5354
+ && 'onmouseleave' in docEl;
5355
+
5356
+
5357
+
5358
+ var isIELegacyEvent = function(event) { return false; };
5359
+
5360
+ if (window.attachEvent) {
5361
+ if (window.addEventListener) {
5362
+ isIELegacyEvent = function(event) {
5363
+ return !(event instanceof window.Event);
5364
+ };
5365
+ } else {
5366
+ isIELegacyEvent = function(event) { return true; };
5367
+ }
5368
+ }
5369
+
5370
+ var _isButton;
5371
+
5372
+ function _isButtonForDOMEvents(event, code) {
5373
+ return event.which ? (event.which === code + 1) : (event.button === code);
5374
+ }
5375
+
5376
+ var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
5377
+ function _isButtonForLegacyEvents(event, code) {
5378
+ return event.button === legacyButtonMap[code];
5379
+ }
5380
+
5381
+ function _isButtonForWebKit(event, code) {
5382
+ switch (code) {
5383
+ case 0: return event.which == 1 && !event.metaKey;
5384
+ case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
5385
+ case 2: return event.which == 3;
5386
+ default: return false;
5387
+ }
5388
+ }
5389
+
5390
+ if (window.attachEvent) {
5391
+ if (!window.addEventListener) {
5392
+ _isButton = _isButtonForLegacyEvents;
5393
+ } else {
5394
+ _isButton = function(event, code) {
5395
+ return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
5396
+ _isButtonForDOMEvents(event, code);
5397
+ }
5398
+ }
5399
+ } else if (Prototype.Browser.WebKit) {
5400
+ _isButton = _isButtonForWebKit;
5401
+ } else {
5402
+ _isButton = _isButtonForDOMEvents;
5403
+ }
5404
+
5405
+ function isLeftClick(event) { return _isButton(event, 0) }
5406
+
5407
+ function isMiddleClick(event) { return _isButton(event, 1) }
5408
+
5409
+ function isRightClick(event) { return _isButton(event, 2) }
5410
+
5411
+ function element(event) {
5412
+ event = Event.extend(event);
5413
+
5414
+ var node = event.target, type = event.type,
5415
+ currentTarget = event.currentTarget;
5416
+
5417
+ if (currentTarget && currentTarget.tagName) {
5418
+ if (type === 'load' || type === 'error' ||
5419
+ (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
5420
+ && currentTarget.type === 'radio'))
5421
+ node = currentTarget;
5422
+ }
5423
+
5424
+ if (node.nodeType == Node.TEXT_NODE)
5425
+ node = node.parentNode;
5426
+
5427
+ return Element.extend(node);
5428
+ }
5429
+
5430
+ function findElement(event, expression) {
5431
+ var element = Event.element(event);
5432
+
5433
+ if (!expression) return element;
5434
+ while (element) {
5435
+ if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
5436
+ return Element.extend(element);
5437
+ }
5438
+ element = element.parentNode;
5439
+ }
5440
+ }
5441
+
5442
+ function pointer(event) {
5443
+ return { x: pointerX(event), y: pointerY(event) };
5444
+ }
5445
+
5446
+ function pointerX(event) {
5447
+ var docElement = document.documentElement,
5448
+ body = document.body || { scrollLeft: 0 };
5449
+
5450
+ return event.pageX || (event.clientX +
5451
+ (docElement.scrollLeft || body.scrollLeft) -
5452
+ (docElement.clientLeft || 0));
5453
+ }
5454
+
5455
+ function pointerY(event) {
5456
+ var docElement = document.documentElement,
5457
+ body = document.body || { scrollTop: 0 };
5458
+
5459
+ return event.pageY || (event.clientY +
5460
+ (docElement.scrollTop || body.scrollTop) -
5461
+ (docElement.clientTop || 0));
5462
+ }
5463
+
5464
+
5465
+ function stop(event) {
5466
+ Event.extend(event);
5467
+ event.preventDefault();
5468
+ event.stopPropagation();
5469
+
5470
+ event.stopped = true;
5471
+ }
5472
+
5473
+
5474
+ Event.Methods = {
5475
+ isLeftClick: isLeftClick,
5476
+ isMiddleClick: isMiddleClick,
5477
+ isRightClick: isRightClick,
5478
+
5479
+ element: element,
5480
+ findElement: findElement,
5481
+
5482
+ pointer: pointer,
5483
+ pointerX: pointerX,
5484
+ pointerY: pointerY,
5485
+
5486
+ stop: stop
5487
+ };
5488
+
5489
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
5490
+ m[name] = Event.Methods[name].methodize();
5491
+ return m;
5492
+ });
5493
+
5494
+ if (window.attachEvent) {
5495
+ function _relatedTarget(event) {
5496
+ var element;
5497
+ switch (event.type) {
5498
+ case 'mouseover':
5499
+ case 'mouseenter':
5500
+ element = event.fromElement;
5501
+ break;
5502
+ case 'mouseout':
5503
+ case 'mouseleave':
5504
+ element = event.toElement;
5505
+ break;
5506
+ default:
5507
+ return null;
5508
+ }
5509
+ return Element.extend(element);
5510
+ }
5511
+
5512
+ var additionalMethods = {
5513
+ stopPropagation: function() { this.cancelBubble = true },
5514
+ preventDefault: function() { this.returnValue = false },
5515
+ inspect: function() { return '[object Event]' }
5516
+ };
5517
+
5518
+ Event.extend = function(event, element) {
5519
+ if (!event) return false;
5520
+
5521
+ if (!isIELegacyEvent(event)) return event;
5522
+
5523
+ if (event._extendedByPrototype) return event;
5524
+ event._extendedByPrototype = Prototype.emptyFunction;
5525
+
5526
+ var pointer = Event.pointer(event);
5527
+
5528
+ Object.extend(event, {
5529
+ target: event.srcElement || element,
5530
+ relatedTarget: _relatedTarget(event),
5531
+ pageX: pointer.x,
5532
+ pageY: pointer.y
5533
+ });
5534
+
5535
+ Object.extend(event, methods);
5536
+ Object.extend(event, additionalMethods);
5537
+
5538
+ return event;
5539
+ };
5540
+ } else {
5541
+ Event.extend = Prototype.K;
5542
+ }
5543
+
5544
+ if (window.addEventListener) {
5545
+ Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
5546
+ Object.extend(Event.prototype, methods);
5547
+ }
5548
+
5549
+ function _createResponder(element, eventName, handler) {
5550
+ var registry = Element.retrieve(element, 'prototype_event_registry');
5551
+
5552
+ if (Object.isUndefined(registry)) {
5553
+ CACHE.push(element);
5554
+ registry = Element.retrieve(element, 'prototype_event_registry', $H());
5555
+ }
5556
+
5557
+ var respondersForEvent = registry.get(eventName);
5558
+ if (Object.isUndefined(respondersForEvent)) {
5559
+ respondersForEvent = [];
5560
+ registry.set(eventName, respondersForEvent);
5561
+ }
5562
+
5563
+ if (respondersForEvent.pluck('handler').include(handler)) return false;
5564
+
5565
+ var responder;
5566
+ if (eventName.include(":")) {
5567
+ responder = function(event) {
5568
+ if (Object.isUndefined(event.eventName))
5569
+ return false;
5570
+
5571
+ if (event.eventName !== eventName)
5572
+ return false;
5573
+
5574
+ Event.extend(event, element);
5575
+ handler.call(element, event);
5576
+ };
5577
+ } else {
5578
+ if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
5579
+ (eventName === "mouseenter" || eventName === "mouseleave")) {
5580
+ if (eventName === "mouseenter" || eventName === "mouseleave") {
5581
+ responder = function(event) {
5582
+ Event.extend(event, element);
5583
+
5584
+ var parent = event.relatedTarget;
5585
+ while (parent && parent !== element) {
5586
+ try { parent = parent.parentNode; }
5587
+ catch(e) { parent = element; }
5588
+ }
5589
+
5590
+ if (parent === element) return;
5591
+
5592
+ handler.call(element, event);
5593
+ };
5594
+ }
5595
+ } else {
5596
+ responder = function(event) {
5597
+ Event.extend(event, element);
5598
+ handler.call(element, event);
5599
+ };
5600
+ }
5601
+ }
5602
+
5603
+ responder.handler = handler;
5604
+ respondersForEvent.push(responder);
5605
+ return responder;
5606
+ }
5607
+
5608
+ function _destroyCache() {
5609
+ for (var i = 0, length = CACHE.length; i < length; i++) {
5610
+ Event.stopObserving(CACHE[i]);
5611
+ CACHE[i] = null;
5612
+ }
5613
+ }
5614
+
5615
+ var CACHE = [];
5616
+
5617
+ if (Prototype.Browser.IE)
5618
+ window.attachEvent('onunload', _destroyCache);
5619
+
5620
+ if (Prototype.Browser.WebKit)
5621
+ window.addEventListener('unload', Prototype.emptyFunction, false);
5622
+
5623
+
5624
+ var _getDOMEventName = Prototype.K,
5625
+ translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
5626
+
5627
+ if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
5628
+ _getDOMEventName = function(eventName) {
5629
+ return (translations[eventName] || eventName);
5630
+ };
5631
+ }
5632
+
5633
+ function observe(element, eventName, handler) {
5634
+ element = $(element);
5635
+
5636
+ var responder = _createResponder(element, eventName, handler);
5637
+
5638
+ if (!responder) return element;
5639
+
5640
+ if (eventName.include(':')) {
5641
+ if (element.addEventListener)
5642
+ element.addEventListener("dataavailable", responder, false);
5643
+ else {
5644
+ element.attachEvent("ondataavailable", responder);
5645
+ element.attachEvent("onlosecapture", responder);
5646
+ }
5647
+ } else {
5648
+ var actualEventName = _getDOMEventName(eventName);
5649
+
5650
+ if (element.addEventListener)
5651
+ element.addEventListener(actualEventName, responder, false);
5652
+ else
5653
+ element.attachEvent("on" + actualEventName, responder);
5654
+ }
5655
+
5656
+ return element;
5657
+ }
5658
+
5659
+ function stopObserving(element, eventName, handler) {
5660
+ element = $(element);
5661
+
5662
+ var registry = Element.retrieve(element, 'prototype_event_registry');
5663
+ if (!registry) return element;
5664
+
5665
+ if (!eventName) {
5666
+ registry.each( function(pair) {
5667
+ var eventName = pair.key;
5668
+ stopObserving(element, eventName);
5669
+ });
5670
+ return element;
5671
+ }
5672
+
5673
+ var responders = registry.get(eventName);
5674
+ if (!responders) return element;
5675
+
5676
+ if (!handler) {
5677
+ responders.each(function(r) {
5678
+ stopObserving(element, eventName, r.handler);
5679
+ });
5680
+ return element;
5681
+ }
5682
+
5683
+ var i = responders.length, responder;
5684
+ while (i--) {
5685
+ if (responders[i].handler === handler) {
5686
+ responder = responders[i];
5687
+ break;
5688
+ }
5689
+ }
5690
+ if (!responder) return element;
5691
+
5692
+ if (eventName.include(':')) {
5693
+ if (element.removeEventListener)
5694
+ element.removeEventListener("dataavailable", responder, false);
5695
+ else {
5696
+ element.detachEvent("ondataavailable", responder);
5697
+ element.detachEvent("onlosecapture", responder);
5698
+ }
5699
+ } else {
5700
+ var actualEventName = _getDOMEventName(eventName);
5701
+ if (element.removeEventListener)
5702
+ element.removeEventListener(actualEventName, responder, false);
5703
+ else
5704
+ element.detachEvent('on' + actualEventName, responder);
5705
+ }
5706
+
5707
+ registry.set(eventName, responders.without(responder));
5708
+
5709
+ return element;
5710
+ }
5711
+
5712
+ function fire(element, eventName, memo, bubble) {
5713
+ element = $(element);
5714
+
5715
+ if (Object.isUndefined(bubble))
5716
+ bubble = true;
5717
+
5718
+ if (element == document && document.createEvent && !element.dispatchEvent)
5719
+ element = document.documentElement;
5720
+
5721
+ var event;
5722
+ if (document.createEvent) {
5723
+ event = document.createEvent('HTMLEvents');
5724
+ event.initEvent('dataavailable', bubble, true);
5725
+ } else {
5726
+ event = document.createEventObject();
5727
+ event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
5728
+ }
5729
+
5730
+ event.eventName = eventName;
5731
+ event.memo = memo || { };
5732
+
5733
+ if (document.createEvent)
5734
+ element.dispatchEvent(event);
5735
+ else
5736
+ element.fireEvent(event.eventType, event);
5737
+
5738
+ return Event.extend(event);
5739
+ }
5740
+
5741
+ Event.Handler = Class.create({
5742
+ initialize: function(element, eventName, selector, callback) {
5743
+ this.element = $(element);
5744
+ this.eventName = eventName;
5745
+ this.selector = selector;
5746
+ this.callback = callback;
5747
+ this.handler = this.handleEvent.bind(this);
5748
+ },
5749
+
5750
+ start: function() {
5751
+ Event.observe(this.element, this.eventName, this.handler);
5752
+ return this;
5753
+ },
5754
+
5755
+ stop: function() {
5756
+ Event.stopObserving(this.element, this.eventName, this.handler);
5757
+ return this;
5758
+ },
5759
+
5760
+ handleEvent: function(event) {
5761
+ var element = Event.findElement(event, this.selector);
5762
+ if (element) this.callback.call(this.element, event, element);
5763
+ }
5764
+ });
5765
+
5766
+ function on(element, eventName, selector, callback) {
5767
+ element = $(element);
5768
+ if (Object.isFunction(selector) && Object.isUndefined(callback)) {
5769
+ callback = selector, selector = null;
5770
+ }
5771
+
5772
+ return new Event.Handler(element, eventName, selector, callback).start();
5773
+ }
5774
+
5775
+ Object.extend(Event, Event.Methods);
5776
+
5777
+ Object.extend(Event, {
5778
+ fire: fire,
5779
+ observe: observe,
5780
+ stopObserving: stopObserving,
5781
+ on: on
5782
+ });
5783
+
5784
+ Element.addMethods({
5785
+ fire: fire,
5786
+
5787
+ observe: observe,
5788
+
5789
+ stopObserving: stopObserving,
5790
+
5791
+ on: on
5792
+ });
5793
+
5794
+ Object.extend(document, {
5795
+ fire: fire.methodize(),
5796
+
5797
+ observe: observe.methodize(),
5798
+
5799
+ stopObserving: stopObserving.methodize(),
5800
+
5801
+ on: on.methodize(),
5802
+
5803
+ loaded: false
5804
+ });
5805
+
5806
+ if (window.Event) Object.extend(window.Event, Event);
5807
+ else window.Event = Event;
5808
+ })();
5809
+
5810
+ (function() {
5811
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
5812
+ Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
5813
+
5814
+ var timer;
5815
+
5816
+ function fireContentLoadedEvent() {
5817
+ if (document.loaded) return;
5818
+ if (timer) window.clearTimeout(timer);
5819
+ document.loaded = true;
5820
+ document.fire('dom:loaded');
5821
+ }
5822
+
5823
+ function checkReadyState() {
5824
+ if (document.readyState === 'complete') {
5825
+ document.stopObserving('readystatechange', checkReadyState);
5826
+ fireContentLoadedEvent();
5827
+ }
5828
+ }
5829
+
5830
+ function pollDoScroll() {
5831
+ try { document.documentElement.doScroll('left'); }
5832
+ catch(e) {
5833
+ timer = pollDoScroll.defer();
5834
+ return;
5835
+ }
5836
+ fireContentLoadedEvent();
5837
+ }
5838
+
5839
+ if (document.addEventListener) {
5840
+ document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
5841
+ } else {
5842
+ document.observe('readystatechange', checkReadyState);
5843
+ if (window == top)
5844
+ timer = pollDoScroll.defer();
5845
+ }
5846
+
5847
+ Event.observe(window, 'load', fireContentLoadedEvent);
5848
+ })();
5849
+
5850
+ Element.addMethods();
5851
+
5852
+ /*------------------------------- DEPRECATED -------------------------------*/
5853
+
5854
+ Hash.toQueryString = Object.toQueryString;
5855
+
5856
+ var Toggle = { display: Element.toggle };
5857
+
5858
+ Element.Methods.childOf = Element.Methods.descendantOf;
5859
+
5860
+ var Insertion = {
5861
+ Before: function(element, content) {
5862
+ return Element.insert(element, {before:content});
5863
+ },
5864
+
5865
+ Top: function(element, content) {
5866
+ return Element.insert(element, {top:content});
5867
+ },
5868
+
5869
+ Bottom: function(element, content) {
5870
+ return Element.insert(element, {bottom:content});
5871
+ },
5872
+
5873
+ After: function(element, content) {
5874
+ return Element.insert(element, {after:content});
5875
+ }
5876
+ };
5877
+
5878
+ var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
5879
+
5880
+ var Position = {
5881
+ includeScrollOffsets: false,
5882
+
5883
+ prepare: function() {
5884
+ this.deltaX = window.pageXOffset
5885
+ || document.documentElement.scrollLeft
5886
+ || document.body.scrollLeft
5887
+ || 0;
5888
+ this.deltaY = window.pageYOffset
5889
+ || document.documentElement.scrollTop
5890
+ || document.body.scrollTop
5891
+ || 0;
5892
+ },
5893
+
5894
+ within: function(element, x, y) {
5895
+ if (this.includeScrollOffsets)
5896
+ return this.withinIncludingScrolloffsets(element, x, y);
5897
+ this.xcomp = x;
5898
+ this.ycomp = y;
5899
+ this.offset = Element.cumulativeOffset(element);
5900
+
5901
+ return (y >= this.offset[1] &&
5902
+ y < this.offset[1] + element.offsetHeight &&
5903
+ x >= this.offset[0] &&
5904
+ x < this.offset[0] + element.offsetWidth);
5905
+ },
5906
+
5907
+ withinIncludingScrolloffsets: function(element, x, y) {
5908
+ var offsetcache = Element.cumulativeScrollOffset(element);
5909
+
5910
+ this.xcomp = x + offsetcache[0] - this.deltaX;
5911
+ this.ycomp = y + offsetcache[1] - this.deltaY;
5912
+ this.offset = Element.cumulativeOffset(element);
5913
+
5914
+ return (this.ycomp >= this.offset[1] &&
5915
+ this.ycomp < this.offset[1] + element.offsetHeight &&
5916
+ this.xcomp >= this.offset[0] &&
5917
+ this.xcomp < this.offset[0] + element.offsetWidth);
5918
+ },
5919
+
5920
+ overlap: function(mode, element) {
5921
+ if (!mode) return 0;
5922
+ if (mode == 'vertical')
5923
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
5924
+ element.offsetHeight;
5925
+ if (mode == 'horizontal')
5926
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
5927
+ element.offsetWidth;
5928
+ },
5929
+
5930
+
5931
+ cumulativeOffset: Element.Methods.cumulativeOffset,
5932
+
5933
+ positionedOffset: Element.Methods.positionedOffset,
5934
+
5935
+ absolutize: function(element) {
5936
+ Position.prepare();
5937
+ return Element.absolutize(element);
5938
+ },
5939
+
5940
+ relativize: function(element) {
5941
+ Position.prepare();
5942
+ return Element.relativize(element);
5943
+ },
5944
+
5945
+ realOffset: Element.Methods.cumulativeScrollOffset,
5946
+
5947
+ offsetParent: Element.Methods.getOffsetParent,
5948
+
5949
+ page: Element.Methods.viewportOffset,
5950
+
5951
+ clone: function(source, target, options) {
5952
+ options = options || { };
5953
+ return Element.clonePosition(target, source, options);
5954
+ }
5955
+ };
5956
+
5957
+ /*--------------------------------------------------------------------------*/
5958
+
5959
+ if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
5960
+ function iter(name) {
5961
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
5962
+ }
5963
+
5964
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
5965
+ function(element, className) {
5966
+ className = className.toString().strip();
5967
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
5968
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
5969
+ } : function(element, className) {
5970
+ className = className.toString().strip();
5971
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
5972
+ if (!classNames && !className) return elements;
5973
+
5974
+ var nodes = $(element).getElementsByTagName('*');
5975
+ className = ' ' + className + ' ';
5976
+
5977
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
5978
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
5979
+ (classNames && classNames.all(function(name) {
5980
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
5981
+ }))))
5982
+ elements.push(Element.extend(child));
5983
+ }
5984
+ return elements;
5985
+ };
5986
+
5987
+ return function(className, parentElement) {
5988
+ return $(parentElement || document.body).getElementsByClassName(className);
5989
+ };
5990
+ }(Element.Methods);
5991
+
5992
+ /*--------------------------------------------------------------------------*/
5993
+
5994
+ Element.ClassNames = Class.create();
5995
+ Element.ClassNames.prototype = {
5996
+ initialize: function(element) {
5997
+ this.element = $(element);
5998
+ },
5999
+
6000
+ _each: function(iterator) {
6001
+ this.element.className.split(/\s+/).select(function(name) {
6002
+ return name.length > 0;
6003
+ })._each(iterator);
6004
+ },
6005
+
6006
+ set: function(className) {
6007
+ this.element.className = className;
6008
+ },
6009
+
6010
+ add: function(classNameToAdd) {
6011
+ if (this.include(classNameToAdd)) return;
6012
+ this.set($A(this).concat(classNameToAdd).join(' '));
6013
+ },
6014
+
6015
+ remove: function(classNameToRemove) {
6016
+ if (!this.include(classNameToRemove)) return;
6017
+ this.set($A(this).without(classNameToRemove).join(' '));
6018
+ },
6019
+
6020
+ toString: function() {
6021
+ return $A(this).join(' ');
6022
+ }
6023
+ };
6024
+
6025
+ Object.extend(Element.ClassNames.prototype, Enumerable);
6026
+
6027
+ /*--------------------------------------------------------------------------*/
6028
+
6029
+ (function() {
6030
+ window.Selector = Class.create({
6031
+ initialize: function(expression) {
6032
+ this.expression = expression.strip();
6033
+ },
6034
+
6035
+ findElements: function(rootElement) {
6036
+ return Prototype.Selector.select(this.expression, rootElement);
6037
+ },
6038
+
6039
+ match: function(element) {
6040
+ return Prototype.Selector.match(element, this.expression);
6041
+ },
6042
+
6043
+ toString: function() {
6044
+ return this.expression;
6045
+ },
6046
+
6047
+ inspect: function() {
6048
+ return "#<Selector: " + this.expression + ">";
6049
+ }
6050
+ });
6051
+
6052
+ Object.extend(Selector, {
6053
+ matchElements: function(elements, expression) {
6054
+ var match = Prototype.Selector.match,
6055
+ results = [];
6056
+
6057
+ for (var i = 0, length = elements.length; i < length; i++) {
6058
+ var element = elements[i];
6059
+ if (match(element, expression)) {
6060
+ results.push(Element.extend(element));
6061
+ }
6062
+ }
6063
+ return results;
6064
+ },
6065
+
6066
+ findElement: function(elements, expression, index) {
6067
+ index = index || 0;
6068
+ var matchIndex = 0, element;
6069
+ for (var i = 0, length = elements.length; i < length; i++) {
6070
+ element = elements[i];
6071
+ if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
6072
+ return Element.extend(element);
6073
+ }
6074
+ }
6075
+ },
6076
+
6077
+ findChildElements: function(element, expressions) {
6078
+ var selector = expressions.toArray().join(', ');
6079
+ return Prototype.Selector.select(selector, element || document);
6080
+ }
6081
+ });
6082
+ })();
skin/adminhtml/default/default/wisepricer/wisepricer.css ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wp-save-btn {
2
+ background: url("../images/wisepricer/wp-save-btn.png") no-repeat scroll 0 0 transparent;
3
+ border: medium none;
4
+ height: 31px;
5
+ width: 111px;
6
+ }
7
+
8
+ .wp-save-btn:hover{
9
+ background: url("../images/wisepricer/wp-save-btn.png") no-repeat scroll 0 -35px transparent;
10
+ }
11
+ .wp-save-btn:active{
12
+ background: url("../images/wisepricer/wp-save-btn.png") no-repeat scroll 0 -70px transparent;
13
+ }
14
+ .top_block .wp-save-btn{margin-top: 15px;}
15
+ .top_block{float:right;width:100%}
16
+ .mid_block{
17
+ border-bottom: 1px solid #808080;
18
+ border-top: 1px solid #808080;
19
+ float: right;
20
+ margin: 10px 0 15px;
21
+ padding: 20px 0;
22
+ width: 100%;
23
+ }
24
+ .bott_block{float:right;width:100%}
25
+ .top_block_left{float:left;width:45%}
26
+ .top_block_right{float:right;}
27
+ .mid_block_left{float:left;width: 45%;}
28
+ .mid_block_right{float:right;width: 52%;}
29
+ .bott_block_left{float:left;}
30
+ .bott_block_right{float:right;}
31
+ .guidelines{width: 420px;float:right;padding-top: 20px}
32
+ .values-block{width: 580px;position: relative}
33
+ .license-block{ background: none repeat scroll 0 0 #F1F1F1; padding: 20px; width: 410px;margin-bottom: 20px;}
34
+ .license-block label{}
35
+ .license-block .label-block{margin-bottom: 10px;}
36
+ .license-block input{width: 220px;}
37
+ .mid_block .t-header td{ padding-bottom: 10px;}
38
+ .mid_block .input-ele,.mid_block td{vertical-align: top; padding-bottom: 10px}
39
+ .mid_block .desc{
40
+ color: #8C8C8C;
41
+ font-size: 11px;
42
+ vertical-align: bottom;
43
+ margin-top: 0;
44
+ }
45
+ .mid_block .desc p{margin-left:10px}
46
+ .mid_block .field{color:#F69625}
47
+ .guidelines-block ul li{
48
+ background: url("../images/wisepricer/bullet-green.png") no-repeat scroll 0 4px transparent;
49
+ margin-bottom: 10px;
50
+ padding-left: 15px;
51
+ }
52
+ .mapping-guidelines{margin-top:40px }
53
+ .ship-price-cont{
54
+ background: none repeat scroll 0 0 #F1F1F1;
55
+ float: left;
56
+ padding: 10px 10px 5px;
57
+ }
58
+ .ship-price-cont-top{width: 330px;float:left;}
59
+
60
+ .ship-price-cont-bott .label input{ width: 135px;vertical-align:bottom;margin-bottom:7px}
61
+ @media screen and (-webkit-min-device-pixel-ratio:0) { .ship-price-cont-bott .label input { margin-bottom:2px}}
62
+ .ship-price-cont-bott .value input{ width: 135px;vertical-align:bottom;margin-bottom:7px; margin-right: 20px;}
63
+ .ship-price-cont-mid{width: 340px;clear:both;margin:0 0px}
64
+ .ship-price-cont-bott{width: 350px;float:left; margin: 10px 0px 0;}
65
+ .ship-price-cont .label{float:left;}
66
+ .ship-price-cont .value{float:right;margin-right: 4px;}
67
+ .mid_block_right .values-block td.label{width:120px}
68
+ .mid_block .input-ele{width: 230px;}
69
+ .ship-price-cont-mid span{}
70
+ .top_block .inner-cont{
71
+ float: right;
72
+ margin-right: 200px;
73
+ }
74
+ .label input{margin-bottom: 15px;}
75
+ .firstcol{}
76
+ .firstcol span{ margin-left: 5px;}
77
+ .ship-price-cont-bott p{margin-bottom: 0}
78
+ .special-opt-cont .desc{vertical-align: top;}
79
+ .values-block .input-ele .validation-advice{position:absolute;left:350px;background-image: url('../images/wisepricer/validation_advice_bg.gif');}
80
+ .license-block .validation-advice{position:absolute;left:240px;background-image: url('../images/wisepricer/validation_advice_bg.gif');bottom:23px}
81
+
82
+