FourTell_Recommend_S - Version 1.2.8

Version Notes

Google Analytics code for related, crosssells, upsells

Download this release

Release Info

Developer 4Tell Inc
Extension FourTell_Recommend_S
Version 1.2.8
Comparing to
See all releases


Version 1.2.8

Files changed (49) hide show
  1. app/code/community/FourTell/Recommend/Block/Catalog/Product/List/Related.php +141 -0
  2. app/code/community/FourTell/Recommend/Block/Catalog/Product/List/Upsell.php +132 -0
  3. app/code/community/FourTell/Recommend/Block/Checkout/Cart/Crosssell.php +70 -0
  4. app/code/community/FourTell/Recommend/Block/Checkout/Cart/Crosssellcommunity.php +75 -0
  5. app/code/community/FourTell/Recommend/Block/Checkout/Cart/Crosssellenterprise.php +65 -0
  6. app/code/community/FourTell/Recommend/Block/System/Config/Form/Field/Upload.php +19 -0
  7. app/code/community/FourTell/Recommend/Block/System/Config/Google/Analytics/Change.php +8 -0
  8. app/code/community/FourTell/Recommend/Block/System/Config/Google/Analytics/Code.php +10 -0
  9. app/code/community/FourTell/Recommend/Block/System/Config/Google/Analytics/Status.php +13 -0
  10. app/code/community/FourTell/Recommend/Block/Widget.php +366 -0
  11. app/code/community/FourTell/Recommend/Helper/Data.php +293 -0
  12. app/code/community/FourTell/Recommend/Model/Adminhtml/Uploadform.php +403 -0
  13. app/code/community/FourTell/Recommend/Model/DataProcess.php +22 -0
  14. app/code/community/FourTell/Recommend/Model/Observer.php +54 -0
  15. app/code/community/FourTell/Recommend/Model/System/Config/Source/Crosssell.php +22 -0
  16. app/code/community/FourTell/Recommend/Model/System/Config/Source/Enabled.php +22 -0
  17. app/code/community/FourTell/Recommend/Model/System/Config/Source/Mode.php +22 -0
  18. app/code/community/FourTell/Recommend/Model/System/Config/Source/Related.php +22 -0
  19. app/code/community/FourTell/Recommend/Model/System/Config/Source/Reportlevel.php +25 -0
  20. app/code/community/FourTell/Recommend/Model/System/Config/Source/Resell.php +22 -0
  21. app/code/community/FourTell/Recommend/Model/System/Config/Source/Resultformat.php +24 -0
  22. app/code/community/FourTell/Recommend/Model/System/Config/Source/Upsell.php +22 -0
  23. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Clientid.php +24 -0
  24. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Email.php +26 -0
  25. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Maxsalesdataageinmonths.php +24 -0
  26. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Mincommon.php +24 -0
  27. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Minlikelihood.php +24 -0
  28. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Numcrosssell.php +24 -0
  29. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Numrelated.php +24 -0
  30. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Numupsell.php +24 -0
  31. app/code/community/FourTell/Recommend/Model/System/Config/Validation/Owner.php +24 -0
  32. app/code/community/FourTell/Recommend/Model/Upload/Frequency.php +14 -0
  33. app/code/community/FourTell/Recommend/Model/UploadSetting.php +348 -0
  34. app/code/community/FourTell/Recommend/controllers/Adminhtml/UploadformController.php +908 -0
  35. app/code/community/FourTell/Recommend/etc/config.xml +172 -0
  36. app/code/community/FourTell/Recommend/etc/system.xml +304 -0
  37. app/code/community/FourTell/Recommend/etc/upload.xml +12 -0
  38. app/code/community/FourTell/Recommend/etc/widget.xml +111 -0
  39. app/design/adminhtml/default/default/layout/recommend.xml +10 -0
  40. app/design/adminhtml/default/default/template/recommend/uploadform.phtml +625 -0
  41. app/design/frontend/base/default/layout/recommend.xml +28 -0
  42. app/design/frontend/base/default/template/recommend/catalog/product/list/related.phtml +123 -0
  43. app/design/frontend/base/default/template/recommend/catalog/product/list/upsell.phtml +79 -0
  44. app/design/frontend/base/default/template/recommend/checkout/cart/crosssell.phtml +86 -0
  45. app/etc/modules/FourTell_Recommend.xml +9 -0
  46. app/locale/en_US/FourTell_Recommend.csv +1 -0
  47. js/jquery.tools.min.js +21 -0
  48. package.xml +18 -0
  49. skin/frontend/base/default/images/hori_large.png +0 -0
app/code/community/FourTell/Recommend/Block/Catalog/Product/List/Related.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Used to override Mage_Catalog_Block_Product_List_Related
8
+ * Automatically replaces the functionality to get related products
9
+ *
10
+ * @package FourTell_Recommend
11
+ * @copyright 4-Tell, Inc.
12
+ *
13
+ */
14
+ class FourTell_Recommend_Block_Catalog_Product_List_Related extends Mage_Catalog_Block_Product_List_Related {
15
+
16
+ const CACHE_LIFETIME = 86400;
17
+
18
+ protected $_itemCollection;
19
+
20
+ protected function _construct(){
21
+ //parent::__construct();
22
+ //$this->setTemplate('recommend/catalog/product/list/related.phtml');
23
+ $this->setRecommendationType('related');
24
+ }
25
+
26
+ public function getCacheKeyInfo(){
27
+ $cacheInfo = parent::getCacheKeyInfo();
28
+ $cacheInfo[] = Mage::registry('product')->getData('entity_id');
29
+ $cacheInfo[] = implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds());
30
+ $cacheInfo[] = implode(",", Mage::registry('previous_products'));
31
+ $cacheInfo[] = Mage::getSingleton('customer/session')->getCustomerId();
32
+ $cacheInfo[] = "Related";
33
+ $cacheInfo[] = Mage::helper('recommend')->displayNumrelatedEnabled();
34
+ return $cacheInfo;
35
+ }
36
+
37
+ protected function _prepareData() {
38
+ // If extension is not enabled use default functionality
39
+ if (!Mage::helper('recommend')->isEnabled()){
40
+ Mage::Log('recommend module is disabled');
41
+ return parent::_prepareData();
42
+ }
43
+
44
+ // If related recommendations is off the use default functionality
45
+ if (!Mage::helper('recommend')->isDisplayRelatedEnabled()){
46
+ Mage::Log('displaying related recommendations is disabled');
47
+ return parent::_prepareData();
48
+ }
49
+ // Get current product being viewed
50
+ $product = Mage::registry('product');
51
+
52
+ if (!Mage::registry('previous_products')) {
53
+ $previous_products='';
54
+ }
55
+ else{
56
+ $previous_products=implode(",", Mage::registry('previous_products'));
57
+ }
58
+
59
+ // Call the web service to get recommended products
60
+ $ids = Mage::helper('recommend')->getRecommendations(
61
+ $product->getData('entity_id'),
62
+ implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds()),
63
+ $previous_products,
64
+ Mage::getSingleton('customer/session')->getCustomerId(),
65
+ "Related",
66
+ Mage::helper('recommend')->displayNumrelatedEnabled()
67
+ );
68
+ //Mage::Log('related');
69
+ //Mage::Log($ids);
70
+
71
+ if (!Mage::registry('previous_products')) {
72
+ Mage::register('previous_products', $ids);
73
+ }
74
+
75
+ $this->_itemCollection = Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter('entity_id', array('in' => $ids,));
76
+
77
+ Mage::getResourceSingleton('checkout/cart')->addExcludeProductFilter($this->_itemCollection,
78
+ Mage::getSingleton('checkout/session')->getQuoteId()
79
+ );
80
+ $this->_addProductAttributesAndPrices($this->_itemCollection);
81
+
82
+ Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($this->_itemCollection);
83
+ //arrange final collection
84
+ if(is_array($ids) && count($ids)>0){
85
+ $final_collection=Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter('entity_id', -1);
86
+ foreach($ids as $id){
87
+ $item=Mage::getModel('catalog/product')->load($id);
88
+ if($item->getId()){
89
+ $final_collection->addItem($item);
90
+ Mage::log('related_item_id: '.$item->getId());
91
+ }
92
+ else{
93
+ Mage::Log('no related items found in db; id: '.$id);
94
+ }
95
+ }
96
+ $this->_itemCollection=$final_collection;
97
+ }
98
+ ///arrange final collection
99
+ $this->_itemCollection->load();
100
+
101
+ foreach ($this->_itemCollection as $product) {
102
+ $product->setDoNotUseCategoryId(true);
103
+ }
104
+
105
+ return $this;
106
+ }
107
+
108
+ protected function _beforeToHtml() {
109
+ $this->_prepareData();
110
+ return parent::_beforeToHtml();
111
+ }
112
+
113
+ public function getItems() {
114
+ return $this->_itemCollection;
115
+ }
116
+
117
+ public function hasItems() {
118
+ return True;
119
+ }
120
+
121
+ public function getItemCollection() {
122
+ return $this->_itemCollection;
123
+ }
124
+
125
+ /**
126
+ * Return true if at least one of the related products can be selected by checkbox, othervise
127
+ false
128
+ *
129
+ * @return bool
130
+ */
131
+ public function isSelectableProductsExist()
132
+ {
133
+ foreach ($this->getItems() as $_item) {
134
+ if(!$_item->isComposite() && $_item->isSaleable() && !$_item->getRequiredOptions()) {
135
+ return true;
136
+ }
137
+ }
138
+ return false;
139
+ }
140
+
141
+ }
app/code/community/FourTell/Recommend/Block/Catalog/Product/List/Upsell.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Used to override Mage_Catalog_Block_Product_List_Upsell
8
+ * Automatically replaces the functionality to get upsell products
9
+ *
10
+ * @package FourTell_Recommend
11
+ * @copyright 4-Tell, Inc.
12
+ *
13
+ */
14
+ class FourTell_Recommend_Block_Catalog_Product_List_Upsell extends Mage_Catalog_Block_Product_List_Upsell {
15
+
16
+ const CACHE_LIFETIME=86400;
17
+
18
+ protected $_itemCollection;
19
+
20
+ public function __construct(){
21
+ //parent::__construct();
22
+ //$this->setTemplate('fourtell/product/list/upsell.phtml');
23
+ $this->setRecommendationType('upsell');
24
+ }
25
+
26
+ public function getCacheKeyInfo(){
27
+ $cacheInfo = parent::getCacheKeyInfo();
28
+ $cacheInfo[] = Mage::registry('product')->getData('entity_id');
29
+ $cacheInfo[] = implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds());
30
+ $cacheInfo[] = implode(",", Mage::registry('previous_products'));
31
+ $cacheInfo[] = Mage::getSingleton('customer/session')->getCustomerId();
32
+ $cacheInfo[] = "Upsell";
33
+ $cacheInfo[] = Mage::getStoreConfig('recommend/display_recommendation/numupsell');
34
+ return $cacheInfo;
35
+ }
36
+
37
+ protected function _prepareData() {
38
+ // If extension is not enabled use default functionality
39
+ if (!Mage::helper('recommend')->isEnabled()){
40
+ return parent::_prepareData();
41
+ }
42
+
43
+ // If upsell recommendations is off the use default functionality
44
+ if (!Mage::helper('recommend')->isDisplayUpsellEnabled()){
45
+ return parent::_prepareData();
46
+ }
47
+
48
+ // Get current product being viewed
49
+ $product = Mage::registry('product');
50
+
51
+ if (!Mage::registry('previous_products')) {
52
+ $previous_products='';
53
+ }
54
+ else{
55
+ $previous_products=implode(",", Mage::registry('previous_products'));
56
+ }
57
+ // Call the web service to get recommended products
58
+ $ids = Mage::helper('recommend')->getRecommendations(
59
+ $product->getData('entity_id'),
60
+ implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds()),
61
+ $previous_products,
62
+ Mage::getSingleton('customer/session')->getCustomerId(),
63
+ "Upsell",
64
+ Mage::helper('recommend')->displayNumupsell()
65
+ );
66
+ //Mage::Log('Upsell');
67
+ //Mage::Log($ids);
68
+ if (!Mage::registry('previous_products')) {
69
+ Mage::register('previous_products', $ids);
70
+ }
71
+
72
+ $this->_itemCollection = Mage::getModel('catalog/product')->getCollection()
73
+ ->addAttributeToFilter('entity_id', array(
74
+ 'in' => $ids,
75
+ ));
76
+ Mage::getResourceSingleton('checkout/cart')->addExcludeProductFilter($this->_itemCollection,
77
+ Mage::getSingleton('checkout/session')->getQuoteId()
78
+ );
79
+ $this->_addProductAttributesAndPrices($this->_itemCollection);
80
+
81
+ Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($this->_itemCollection);
82
+ //arrange final collection
83
+ if(is_array($ids) && count($ids)>0){
84
+ $final_collection=Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter('entity_id', -1);
85
+ foreach($ids as $id){
86
+ $item=Mage::getModel('catalog/product')->load($id);
87
+ if($item->getId()){
88
+ $final_collection->addItem($item);
89
+ Mage::log('upsell_item_id: '.$item->getId());
90
+ }
91
+ else{
92
+ Mage::Log('no upsell items found in db; id: '.$id);
93
+ }
94
+ }
95
+ $this->_itemCollection=$final_collection;
96
+ }
97
+ ///arrange final collection
98
+
99
+ $this->_itemCollection->load();
100
+ $this->_items=$this->_itemCollection;
101
+ foreach ($this->_itemCollection as $product) {
102
+ $product->setDoNotUseCategoryId(true);
103
+ }
104
+
105
+ return $this;
106
+ }
107
+ /*
108
+ private function getCacheKeyInfo(){
109
+
110
+ }
111
+ */
112
+ protected function _beforeToHtml() {
113
+ //$this->_prepareData();
114
+ return parent::_beforeToHtml();
115
+ }
116
+
117
+ public function getItems() {
118
+ if (true || is_null($this->_items)) {
119
+ $this->_items = $this->getItemCollection()->getItems();
120
+ }
121
+ return $this->_items;
122
+ }
123
+
124
+ public function hasItems() {
125
+ return true;
126
+ }
127
+ /*
128
+ public function getItemCollection() {
129
+ return $this->_itemCollection;
130
+ }
131
+ */
132
+ }
app/code/community/FourTell/Recommend/Block/Checkout/Cart/Crosssell.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Used to override Mage_Checkout_Block_Cart_Crosssell
8
+ * Automatically replaces the functionality to get cross sell products
9
+ *
10
+ * @package FourTell_Recommend
11
+ * @copyright 4-Tell, Inc.
12
+ *
13
+ */
14
+ class FourTell_Recommend_Block_Checkout_Cart_Crosssell extends Mage_Checkout_Block_Cart_Crosssell {
15
+
16
+ const CACHE_LIFETIME=86400;
17
+
18
+ protected $_items = null;
19
+
20
+ public function __construct(){
21
+ $this->setRecommendationType('crosssell');
22
+ }
23
+
24
+ public function getCacheKeyInfo(){
25
+ $cacheInfo = parent::getCacheKeyInfo();
26
+ $cacheInfo[] = implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds());
27
+ $cacheInfo[] = Mage::getSingleton('customer/session')->getCustomerId();
28
+ $cacheInfo[] = "Crosssell";
29
+ $cacheInfo[] = Mage::helper('recommend')->displayNumcrosssell();
30
+ return $cacheInfo;
31
+ }
32
+
33
+ public function getItems(){
34
+ if (!$this->_items){
35
+ $items = array();
36
+ // If extension is not enabled use default functionality
37
+ if (!Mage::helper('recommend')->isEnabled()){
38
+ return parent::getItems();
39
+ }
40
+ // Get current product being viewed
41
+ $product = Mage::registry('product');
42
+ $productId='';
43
+ if($product){
44
+ $productId=$product->getData('entity_id');
45
+ }
46
+ // Call the web service to get recommended products
47
+ $ids = Mage::helper('recommend')->getRecommendations(
48
+ $productId,
49
+ implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds()),
50
+ "",
51
+ Mage::getSingleton('customer/session')->getCustomerId(),
52
+ "Crosssell",
53
+ Mage::helper('recommend')->displayNumcrosssell()
54
+ );
55
+ // Load each product and add to the collection
56
+ foreach ($ids as $id) {
57
+ $item=Mage::getModel('catalog/product')->load($id);
58
+ if($item->getId()){
59
+ $items[] = $item;
60
+ Mage::log('crosssell_item_id: '.$item->getId());
61
+ }
62
+ else{
63
+ Mage::Log('no crosssell items found in db; id: '.$id);
64
+ }
65
+ }
66
+ $this->_items = $items;
67
+ }
68
+ return $this->_items;
69
+ }
70
+ }
app/code/community/FourTell/Recommend/Block/Checkout/Cart/Crosssellcommunity.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Used to override Mage_Checkout_Block_Cart_Crosssell
8
+ * Automatically replaces the functionality to get cross sell products
9
+ *
10
+ * @package FourTell_Recommend
11
+ * @copyright 4-Tell, Inc.
12
+ *
13
+ */
14
+ class FourTell_Recommend_Block_Checkout_Cart_Crosssellcommunity extends Mage_Checkout_Block_Cart_Crosssell {
15
+
16
+ const CACHE_LIFETIME=86400;
17
+
18
+ protected $_items = null;
19
+
20
+ /*
21
+ public function __construct(){
22
+ parent::__construct();
23
+ $this->setTemplate('fourtell/checkout/cart/crosssell.phtml');
24
+ }
25
+ */
26
+
27
+ public function getCacheKeyInfo(){
28
+ $cacheInfo = parent::getCacheKeyInfo();
29
+ $cacheInfo[] = implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds());
30
+ $cacheInfo[] = Mage::getSingleton('customer/session')->getCustomerId();
31
+ $cacheInfo[] = "Crosssell";
32
+ $cacheInfo[] = Mage::helper('recommend')->displayNumcrosssell();
33
+ return $cacheInfo;
34
+ }
35
+
36
+ public function getItemCollection() {
37
+ return $this->getItems();
38
+ }
39
+
40
+ public function getItems() {
41
+ if (!$this->_items) {
42
+ $items = array();
43
+
44
+ // If extension is not enabled use default functionality
45
+ if (!Mage::helper('recommend')->isEnabled())
46
+ return parent::getItems();
47
+
48
+ // Get current product being viewed
49
+ $product = Mage::registry('product');
50
+ $productId='';
51
+ if($product){
52
+ $productId=$product->getData('entity_id');
53
+ }
54
+ // Call the web service to get recommended products
55
+ $ids = Mage::helper('recommend')->getRecommendations(
56
+ $productId,
57
+ implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds()),
58
+ "",
59
+ Mage::getSingleton('customer/session')->getCustomerId(),
60
+ "Crosssell",
61
+ Mage::helper('recommend')->displayNumcrosssell()
62
+ );
63
+ Mage::Log("CrosssellCommunity");
64
+ Mage::Log($ids);
65
+ // Load each product and add to the collection
66
+ foreach ($ids as $id) {
67
+ $items[] = Mage::getModel('catalog/product')->load($id);
68
+ }
69
+ $this->_items = $items;
70
+ return $this->_items;
71
+ }
72
+ return $this->_items;
73
+ }
74
+
75
+ }
app/code/community/FourTell/Recommend/Block/Checkout/Cart/Crosssellenterprise.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * Used to override Mage_Checkout_Block_Cart_Crosssell
7
+ * Automatically replaces the functionality to get cross sell products
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Block_Checkout_Cart_Crosssellenterprise extends Enterprise_TargetRule_Block_Checkout_Cart_Crosssell {
14
+
15
+ const CACHE_LIFETIME = 86400;
16
+
17
+ protected $_items = null;
18
+
19
+ public function getCacheKeyInfo(){
20
+ $cacheInfo = parent::getCacheKeyInfo();
21
+ $cacheInfo[] = implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds());
22
+ $cacheInfo[] = Mage::getSingleton('customer/session')->getCustomerId();
23
+ $cacheInfo[] = "Crosssell";
24
+ $cacheInfo[] = Mage::helper('recommend')->displayNumcrosssell();
25
+ return $cacheInfo;
26
+ }
27
+
28
+ public function getItemCollection() {
29
+ return $this->getItems();
30
+ }
31
+
32
+ public function getItems() {
33
+ if (!$this->_items) {
34
+ $items = array();
35
+ // If extension is not enabled use default functionality
36
+ if (!Mage::helper('recommend')->isEnabled())
37
+ return parent::getItems();
38
+
39
+ // Get current product being viewed
40
+ $product = Mage::registry('product');
41
+ $productId='';
42
+ if($product){
43
+ $productId=$product->getData('entity_id');
44
+ }
45
+ // Call the web service to get recommended products
46
+ $ids = Mage::helper('recommend')->getRecommendations(
47
+ $productId,
48
+ implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds()),
49
+ "",
50
+ Mage::getSingleton('customer/session')->getCustomerId(),
51
+ "Crosssell",
52
+ Mage::helper('recommend')->displayNumcrosssell()
53
+ );
54
+ Mage::Log("CrosssellEnterprise");
55
+ Mage::Log($ids);
56
+ // Load each product and add to the collection
57
+ foreach ($ids as $id) {
58
+ $items[] = Mage::getModel('catalog/product')->load($id);
59
+ }
60
+ $this->_items = $items;
61
+ }
62
+ return $this->_items;
63
+ }
64
+
65
+ }
app/code/community/FourTell/Recommend/Block/System/Config/Form/Field/Upload.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Create a link in system config to the 4-Tell site
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Block_System_Config_Form_Field_Upload extends Mage_Adminhtml_Block_System_Config_Form_Field {
14
+
15
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
16
+ return "<a href='" . Mage::helper('adminhtml')->getUrl("recommend/adminhtml_uploadform/index/") . "'>".Mage::helper('recommend')->__('Upload Settings and Upload Data')."</a>";
17
+ }
18
+
19
+ }
app/code/community/FourTell/Recommend/Block/System/Config/Google/Analytics/Change.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FourTell_Recommend_Block_System_Config_Google_Analytics_Change extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
6
+ return "<a href='" . Mage::helper('adminhtml')->getUrl("adminhtml/system_config/edit/section/google/") . "'>".Mage::helper('recommend')->__('Change')."</a>";
7
+ }
8
+ }
app/code/community/FourTell/Recommend/Block/System/Config/Google/Analytics/Code.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FourTell_Recommend_Block_System_Config_Google_Analytics_Code extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
6
+ //return "<a href='" . Mage::helper('adminhtml')->getUrl("recommend/adminhtml_uploadform/index/") . "'>".Mage::helper('recommend')->__('Upload Settings and Upload Data')."</a>";
7
+ //return "Yes";
8
+ return Mage::getStoreConfig('google/analytics/account');
9
+ }
10
+ }
app/code/community/FourTell/Recommend/Block/System/Config/Google/Analytics/Status.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FourTell_Recommend_Block_System_Config_Google_Analytics_Status extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
6
+ if(Mage::getStoreConfig('google/analytics/active', null)){
7
+ return "Yes";
8
+ }
9
+ else{
10
+ return "No";
11
+ }
12
+ }
13
+ }
app/code/community/FourTell/Recommend/Block/Widget.php ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ */
9
+ class FourTell_Recommend_Block_Widget extends Mage_Core_Block_Abstract implements Mage_Widget_Block_Interface {
10
+
11
+ protected function _toHtml() {
12
+ // If extension is not enabled then return
13
+ if (Mage::getStoreConfig('recommend/config/enabled') != "1")
14
+ return '';
15
+
16
+ $maxProducts = 20;
17
+ //for js scroller
18
+ if($this->getData('in_line')){
19
+ $in_row=$this->getData('in_line');
20
+ }
21
+ else{
22
+ $in_row=3;
23
+ }
24
+
25
+ // Get the current product being viewed from the registry
26
+ $product = Mage::registry('product');
27
+
28
+ $pId = "";
29
+ if($product){
30
+ try {
31
+ $pId = $product->getData('entity_id');
32
+ } catch (Exception $e) {
33
+
34
+ }
35
+ }
36
+ $cartIds = implode(",", Mage::helper('checkout/cart')->getCart()->getProductIds());
37
+
38
+ $page = "Unknown";
39
+
40
+ // Check for search results page
41
+ if (strtolower(Mage::app()->getRequest()->getRouteName()) == "catalogsearch") {
42
+ // Get the product currently being viewed, this might not be all
43
+ // products, it could be the products of the current page
44
+ $sp = Mage::registry('current_layer')->getProductCollection();
45
+
46
+ // Get up to the first 5 products to send to the 4-Tell service
47
+ $result = array();
48
+ foreach ($sp as $p) {
49
+ if (count($result) < $maxProducts) {
50
+ $result[] = $p->getId();
51
+ }
52
+ }
53
+
54
+ $param1 = implode(",", $result);
55
+ $param2 = $cartIds;
56
+ $page = "Search";
57
+
58
+ // Check for category pages
59
+ } else if (strtolower(Mage::app()->getRequest()->getRouteName()) == "catalog") {
60
+ // Get the product currently being viewed, this might not be all
61
+ // products, it could be the products of the current page
62
+ $currentCategory = Mage::registry('current_category');
63
+ if (is_object($product)) {
64
+
65
+ $param1 = $pId;
66
+ $param2 = $cartIds;
67
+ $page = "Product";
68
+ } else if (is_object($currentCategory)) {
69
+ $collection = $currentCategory->getProductCollection();
70
+
71
+ // Get up to the first 5 products to send to the 4-Tell service
72
+ $result = array();
73
+ foreach ($collection as $product) {
74
+ if (count($result) < $maxProducts) {
75
+ $result[] = $product->getId();
76
+ }
77
+ }
78
+
79
+ $param1 = implode(",", $result);
80
+ $param2 = $cartIds;
81
+ $page = "Category";
82
+ } else {
83
+ $param1 = "";
84
+ $param2 = $cartIds;
85
+ }
86
+
87
+ // Check for checkout pages
88
+ } else if (strtolower(Mage::app()->getRequest()->getRouteName()) != "checkout") {
89
+
90
+ $param1 = $cartIds;
91
+ $param2 = "";
92
+ $page = "Checkout";
93
+
94
+ // Default is a product page
95
+ } else {
96
+
97
+ $param1 = $pId;
98
+ $param2 = $cartIds;
99
+ $page = "Product";
100
+ }
101
+
102
+ $ids = Mage::helper('recommend')->getRecommendations(
103
+ $param1,
104
+ $param2,
105
+ "",
106
+ Mage::getSingleton('customer/session')->getCustomerId(),
107
+ $this->getData('recommendation_type'),
108
+ $this->getData('num_recommendations')
109
+ );
110
+
111
+ if (!is_array($ids) || empty($ids)) {
112
+ return "";
113
+ }
114
+
115
+ // Load each product that came from 4-Tell and add to array
116
+ $collection = array();
117
+ foreach ($ids as $id) {
118
+ $_product = Mage::getModel('catalog/product')->load($id);
119
+ // Check if this product exists
120
+ if( $_product->getId() ) {
121
+ $collection[] = $_product;
122
+ }
123
+ }
124
+
125
+ // Default 4-Tell CSS styles, the name of this
126
+ // class can be overridden in the widget options
127
+ $str = "
128
+ <style>
129
+ .fourtell_widget {
130
+ clear: both;
131
+ width: ".((147*$in_row)+(($this->getData('slider_enable')==1) ? 102 : ''))."px !important;/*690px*//*470px*//*542*/
132
+ height: 211px;
133
+ border: 1px solid #E4E0D5;
134
+ }
135
+
136
+ .fourtell_widget .best-selling {
137
+ padding-left: 5px;
138
+ }
139
+
140
+ .fourtell_widget .widget_product {
141
+ float: left;
142
+ clear: none;
143
+ width: 135px;
144
+ height: 195px;
145
+ padding: 10px 5px 0 5px;
146
+ border: 1px solid #E5DCC3;
147
+ background: #ffffff;
148
+ }
149
+
150
+ .fourtell_widget .widget_product a {
151
+ margin-left: 0px;
152
+ border-color: #E5DCC3;
153
+ color: #1E7EC8;
154
+ text-decoration: underline;
155
+ }
156
+
157
+ .fourtell_widget .widget_product a img {
158
+ padding: 10px;
159
+ margin-bottom: 10px;
160
+ border: 0px solid #E5DCC3;
161
+ height: 75px;
162
+ width: 75px;
163
+ background-color: #ffffff;
164
+ }
165
+
166
+ .fourtell_widget .widget_product h3 {
167
+ height: 50px;
168
+ overflow: hidden;
169
+ }
170
+
171
+ .fourtell_widget .widget_product h3 a {
172
+ color: #0A263C;
173
+ font-weight: bold;
174
+ font-size: 12px;
175
+ }
176
+
177
+ .fourtell_widget .powered_by {
178
+ float: left;
179
+ clear: both;
180
+ font-size: smaller;
181
+ }
182
+
183
+ .fourtell_widget .widget_product .powered_by a {
184
+ color: #000000;
185
+ }
186
+
187
+
188
+ /*
189
+ root element for the scrollable.
190
+ when scrolling occurs this element stays still.
191
+ */
192
+ .scrollable {
193
+
194
+ /* required settings */
195
+ position:relative;
196
+ overflow:hidden;
197
+ width: ".(147*$in_row)."px;/*588*/
198
+ height:210px;
199
+ }
200
+
201
+ /*
202
+ root element for scrollable items. Must be absolutely positioned
203
+ and it should have a extremely large width to accommodate scrollable items.
204
+ it's enough that you set width and height for the root element and
205
+ not for this element.
206
+ */
207
+ .scrollable .items {
208
+ /* this cannot be too large */
209
+ width:20000em;
210
+ position:absolute;
211
+ }
212
+ .scrollable .items div{
213
+ margin-top:1px;
214
+ }
215
+ /*
216
+ a single item. must be floated in horizontal scrolling.
217
+ typically, this element is the one that *you* will style
218
+ the most.
219
+ */
220
+ .items div {
221
+ float:left;
222
+ }
223
+
224
+ /* this makes it possible to add next button beside scrollable */
225
+ .scrollable {
226
+ float:left;
227
+ }
228
+
229
+ /* prev, next, prevPage and nextPage buttons */
230
+ a.browse {
231
+ background:url(".'/skin/frontend/base/default/images/hori_large.png'.") no-repeat;
232
+ display:block;
233
+ width:30px;
234
+ height:30px;
235
+ float:left;
236
+ margin:40px 10px;
237
+ cursor:pointer;
238
+ font-size:1px;
239
+ }
240
+
241
+ /* right */
242
+ a.right { background-position: 0 -30px; clear:right; margin-right: 10px; margin-top: 80px;}
243
+ a.right:hover { background-position:-30px -30px; }
244
+ a.right:active { background-position:-60px -30px; }
245
+
246
+
247
+ /* left */
248
+ a.left { margin-left: 10px; margin-top: 80px;}
249
+ a.left:hover { background-position:-30px 0; }
250
+ a.left:active { background-position:-60px 0; }
251
+
252
+ /* up and down */
253
+ a.up, a.down {
254
+ background:url(../img/scrollable/arrow/vert_large.png) no-repeat;
255
+ float: none;
256
+ margin: 10px 50px;
257
+ }
258
+
259
+ /* up */
260
+ a.up:hover { background-position:-30px 0; }
261
+ a.up:active { background-position:-60px 0; }
262
+
263
+ /* down */
264
+ a.down { background-position: 0 -30px; }
265
+ a.down:hover { background-position:-30px -30px; }
266
+ a.down:active { background-position:-60px -30px; }
267
+
268
+
269
+ /* disabled navigational button */
270
+ a.disabled {
271
+ visibility:hidden !important;
272
+ }
273
+ </style>";
274
+
275
+ //$str.="<link rel=\"stylesheet\" type=\"text/css\" href=\"http://static.flowplayer.org/tools/css/scrollable-horizontal.css\" />";
276
+ /*$str.="<link rel=\"stylesheet\" type=\"text/css\" href=\"http://static.flowplayer.org/tools/css/scrollable-buttons.css\" />";*/
277
+
278
+ /*$str.="<script src=\"http://cdn.jquerytools.org/1.2.6/jquery.tools.min.js\"></script>";*/
279
+ /*$str.="<script>jQuery.noConflict();</script>";*/
280
+
281
+ // Check to see if the CSS class was overridden
282
+ $css_class = "fourtell_widget";
283
+ if ($this->getData('cssclass') != "")
284
+ $css_class = $this->getData('cssclass');
285
+
286
+ // Generate recommended products html
287
+ $str .= '<div class="' . $css_class . '">';
288
+
289
+ if ($this->getData('heading') != "") {
290
+ $str .= '<h3 class="best-selling">';
291
+ $str .= $this->getData('heading');
292
+ $str .= '</h3>';
293
+ }
294
+
295
+ // Get Google Account ID for events tracking
296
+ $gaUA = Mage::helper('recommend')->getGoogleAccountId();
297
+ // Get toutType
298
+ $toutType = $this->getRecommendationType();
299
+
300
+ // Agg Google Tracking JS into this div
301
+ if( strlen($gaUA) > 0 ) {
302
+ $str .= '<script type="text/javascript">'
303
+ .'//<![CDATA['
304
+ .'(function() {'
305
+ .' var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;'
306
+ .' ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';'
307
+ .' (document.getElementsByTagName(\'head\')[0] || document.getElementsByTagName(\'body\')[0]).appendChild(ga);'
308
+ .'})();'
309
+ .' var _gaq = _gaq || [];'
310
+ .' _gaq.push([\'_setAccount\', \''.$gaUA.'\']);'
311
+ .'//]]>'
312
+ .'</script>';
313
+ }
314
+ if($this->getData('slider_enable')==1){
315
+ $str.="<!-- \"previous page\" action -->";
316
+ $str.="<a class=\"prev browse left\"></a>";
317
+ }
318
+ $str.="<!-- root element for scrollable -->";
319
+ $str.="<div class=\"scrollable\">";
320
+
321
+ $str.="<!-- root element for the items -->";
322
+ $str.="<div class=\"items\">";
323
+
324
+ $str.="<div>";
325
+
326
+ foreach ($collection as $counter=>$product) {
327
+
328
+ $gafunction = '';
329
+ //build onclick handler
330
+ if( strlen($gaUA) > 0 ) {
331
+ $gafunction='onclick="_gaq.push([\'_trackEvent\', \'4TellRecs\', \''.$toutType.'\', \''.$product->getEntityId().'\']);"';
332
+ }
333
+ if($counter%$in_row==0 && $counter>0){
334
+ $str.="</div><div>";
335
+ }
336
+ $str .= '<div class="widget_product">';
337
+ $str .= '<a ' . $gafunction . ' href="'.Mage::getUrl('').'' . $product->getUrlPath() . '">'.'<img src="'.$product->getThumbnailUrl().'">'.'</a>';
338
+ $str .= '<br />'.'<h3 class="product-name">'.'<a '.$gafunction.' href="'.Mage::getUrl('').$product->getUrlPath().'">'.$product->getData("name").'</a>'.'</h3>';
339
+ $str .= '<div class="price-box">'.'<span class="regular-price">'.'<span class="price">'.Mage::helper('core')->currency($product->getData("price")).'</span>'.'</span>'.'</div>';
340
+ $str .= '</div>';
341
+ }
342
+
343
+ $str.="</div>";
344
+
345
+ $str.="</div>";
346
+ $str.="</div>";
347
+ if($this->getData('slider_enable')==1){
348
+ $str.="<!-- \"next page\" action -->";
349
+ $str.="<a class=\"next browse right\"></a>";
350
+ }
351
+ $str .= '<div class="powered_by">';
352
+ $str .= '<a href="http://www.4-tell.com" target="_blank">Powered by 4-Tell</a>';
353
+ $str .= '</div>';
354
+ $str .= '</div>';
355
+ if($this->getData('slider_enable')==1){
356
+ $str.="<script>";
357
+ $str.="jQuery(function() {";
358
+ $str.="jQuery(\".scrollable\").scrollable();";
359
+ $str.="});";
360
+ $str.="</script>";
361
+ }
362
+ // Return the html string
363
+ return $str;
364
+ }
365
+
366
+ }
app/code/community/FourTell/Recommend/Helper/Data.php ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Helper_Data extends Mage_Core_Helper_Abstract {
11
+
12
+ /**
13
+ * Get the Client ID from the system configuration
14
+ */
15
+ public function getClientId() {
16
+ return Mage::getStoreConfig('recommend/config/client_id');
17
+ }
18
+
19
+ /**
20
+ * Get mode (live or test)
21
+ */
22
+ public function getMode() {
23
+ if (Mage::getStoreConfig('recommend/config/mode') == "Test") {
24
+ return "biz";
25
+ }
26
+
27
+ return "net";
28
+ }
29
+
30
+ /**
31
+ * Convert type string to type id
32
+ *
33
+ * @param type string name of the type
34
+ *
35
+ * @return int id of the type
36
+ */
37
+ public function getTypeId($type) {
38
+ switch (strtolower($type)) {
39
+ case "upsell":
40
+ return 3;
41
+ break;
42
+
43
+ case "related":
44
+ return 0;
45
+ break;
46
+
47
+ case "crosssell":
48
+ return 0;
49
+ break;
50
+
51
+ case "topsellers":
52
+ return 4;
53
+ break;
54
+
55
+ case "personalized":
56
+ return 1;
57
+ break;
58
+
59
+ default:
60
+ return 3;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get the Google Account ID from the system configuration
66
+ */
67
+ public function getGoogleAccountId() {
68
+ return Mage::getStoreConfig('google/analytics/account');
69
+ }
70
+
71
+ public function getGoogleAccountEnabled() {
72
+ return Mage::getStoreConfig('google/analytics/active');
73
+ }
74
+
75
+ function recommendTypeStringToId($type) {
76
+ switch (strtolower($type)) {
77
+ case 'cross sell':
78
+ return 0;
79
+ break;
80
+
81
+ case 'related':
82
+ return 1;
83
+ break;
84
+
85
+ case 'upsell':
86
+ return 2;
87
+ break;
88
+
89
+ case 'similar':
90
+ return 3;
91
+ break;
92
+
93
+ case 'top sellers':
94
+ return 4;
95
+ break;
96
+ }
97
+
98
+ return 1;
99
+ }
100
+
101
+
102
+ /**
103
+ * Convert type string to type id
104
+ *
105
+ * @param productIds
106
+ * @param cartIds
107
+ * @param customerId
108
+ * @param type
109
+ * @param numResults
110
+ *
111
+ * @return array recommended products
112
+ */
113
+ function getRecommendations($productIds, $cartIds, $blockIds, $customerId, $type, $numResults) {
114
+ //Mage::Log('111');
115
+ try {
116
+ $mode = $this->getMode();
117
+ $clientId = $this->getClientId();
118
+ $typeId = $this->getTypeId($type);
119
+
120
+ $service_url = "http://www.4-tell." . $mode .
121
+ "/Boost2.0/rest/GetRecIDs/string" .
122
+ "?clientAlias=" . $clientId .
123
+ "&productIDs=" . $productIds .
124
+ "&cartIDs=" . $cartIds .
125
+ "&blockIDs=" . $blockIds .
126
+ "&customerId=" . $customerId .
127
+ "&numResults=" . $numResults .
128
+ "&resultType=" . $typeId .
129
+ "&format=CommaDelimited";
130
+ Mage::Log($service_url);
131
+ $curl = curl_init($service_url);
132
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
133
+ $curl_response = curl_exec($curl);
134
+ $info = curl_getinfo($curl);
135
+ curl_close($curl);
136
+
137
+ if ($curl_response === false || $info['http_code'] != 200) {
138
+ return array();
139
+ }
140
+
141
+ $IDs = str_ireplace('<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">', '', $curl_response);
142
+ $IDs = str_ireplace('</string>', '', $IDs);
143
+ $IDs = str_ireplace(' ', '', $IDs);
144
+
145
+ return explode(",", $IDs);
146
+ } catch (Exception $e) {
147
+ return array();
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Get and format CfgLink
153
+ *
154
+ * @return string $cfgLink;
155
+ */
156
+ public function getCfgLink() {
157
+ $cfgLink = Mage::helper("adminhtml")->getUrl("admin/system_config/edit/section/recommend");
158
+ $cfgLink = str_replace("//system", "/admin/system", $cfgLink);
159
+
160
+ return $cfgLink;
161
+ }
162
+
163
+ /**
164
+ * Initialize and return Comps array
165
+ *
166
+ * @return array
167
+ */
168
+ public function getCompsArray() {
169
+ return array(
170
+ 'eq' => 'Equal To',
171
+ 'neq' => 'Not Equal To',
172
+ 'lt' => 'Less Than',
173
+ 'lteq' => 'Less Than or Equal To',
174
+ 'gt' => 'Greater Than',
175
+ 'gteq' => 'Greater Than or Equal To',
176
+ 'like' => 'Contains',
177
+ 'nlike' => 'Does Not Contain',
178
+ 'null' => 'Is Null',
179
+ 'notnull' => 'Is Not Null',
180
+ 'in' => 'In',
181
+ 'nin' => 'Not In'
182
+ );
183
+ }
184
+
185
+ /**
186
+ * Initialize and return Options array
187
+ *
188
+ * @return array
189
+ */
190
+ public function getOptionsArray() {
191
+ return array(
192
+ 'upload_sales_data' => 'Upload Sales Data',
193
+ 'upload_product_data' => 'Upload Product Data',
194
+ 'upload_replacement_data' => 'Upload Replacement Data',
195
+ 'upload_exclusion_data' => 'Upload Exclusion Data'
196
+ );
197
+ }
198
+
199
+ public function getNextUploadDate($sf = null) {
200
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
201
+ $ret = 0;
202
+ $keys = array_keys($model->salesFiles);
203
+
204
+ $getNext = 0;
205
+ if ($sf == null) {
206
+ $getNext = 1;
207
+ }
208
+
209
+ foreach ($keys as $idx => $key) {
210
+ if ($getNext == 1) {
211
+ return $key;
212
+ }
213
+
214
+ if ($sf === $key) {
215
+ $getNext = 1;
216
+ }
217
+ }
218
+ return null;
219
+ }
220
+
221
+ /**
222
+ * Compare types $a and $b
223
+ *
224
+ * @param array $a
225
+ * @param array $b
226
+ * @return numeric
227
+ */
228
+ private function compare_type($a, $b) {
229
+ return strnatcmp($a['name'], $b['name']);
230
+ }
231
+
232
+ public function isEnabled() {
233
+ if(Mage::getStoreConfig('recommend/config/enabled') != "1") return false;
234
+ return true;
235
+ }
236
+
237
+ public function isDisplayRelatedEnabled() {
238
+ if(Mage::getStoreConfig('recommend/display_recommendation/related') != "1") return false;
239
+ return true;
240
+ }
241
+
242
+ public function isDisplayUpsellEnabled() {
243
+ if(Mage::getStoreConfig('recommend/display_recommendation/upsell') != "1") return false;
244
+ return true;
245
+ }
246
+
247
+ public function displayNumrelatedEnabled() {
248
+ return Mage::getStoreConfig('recommend/display_recommendation/numrelated');
249
+ }
250
+
251
+ public function displayNumupsell() {
252
+ return Mage::getStoreConfig('recommend/display_recommendation/numupsell');
253
+ }
254
+
255
+ public function displayNumcrosssell() {
256
+ return Mage::getStoreConfig('recommend/display_recommendation/numcrosssell');
257
+ }
258
+
259
+ /*
260
+ * for debugging
261
+ */
262
+ public function draw($txt){
263
+ ob_start();
264
+ print_r($txt);
265
+ $result=ob_get_contents();
266
+ ob_end_clean();
267
+ file_put_contents('d:/temp/php/result_'.microtime().'.txt', $result);
268
+ }
269
+
270
+ public function writeLastUploaded($last_data=false){
271
+ if(!is_dir(Mage::getBaseDir('media').DS.'tmp')){
272
+ mkdir(Mage::getBaseDir('media').DS.'tmp', 0777);
273
+ }
274
+ if(!$last_data) return false;
275
+ $data=serialize($last_data);
276
+ return file_put_contents(Mage::getBaseDir('media').DS.'tmp/last_uploaded.txt', $data);
277
+ }
278
+
279
+ public function getLastUploaded(){
280
+ if(!is_dir(Mage::getBaseDir('media').DS.'tmp')){
281
+ mkdir(Mage::getBaseDir('media').DS.'tmp', 0777);
282
+ return false;
283
+ }
284
+ if(file_exists(Mage::getBaseDir('media').DS.'tmp/last_uploaded.txt')){
285
+ $result=file_get_contents(Mage::getBaseDir('media').DS.'tmp/last_uploaded.txt');
286
+ if(strlen($result)>0){
287
+ return unserialize($result);
288
+ }
289
+ }
290
+ return false;
291
+ }
292
+
293
+ }
app/code/community/FourTell/Recommend/Model/Adminhtml/Uploadform.php ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class FourTell_Recommend_Model_Adminhtml_Uploadform extends Varien_Object
3
+ {
4
+ public $salesFiles = null;
5
+
6
+ protected function _construct(){
7
+ $this->getUploadSettings();
8
+ }
9
+
10
+ public function getUploadSettings() {
11
+ if (!$this->options) {
12
+ $this->options = Mage::getSingleton('FourTell_Recommend_Model_UploadSetting');
13
+ }
14
+ if (!is_object($this->options)) {
15
+ throw new Exception("Unable to get singleton: FourTell_Recommend_Model_UploadSetting");
16
+ }
17
+
18
+ $this->options->load();
19
+ return $this->options;
20
+ }
21
+
22
+ public function processData()
23
+ {
24
+ $post=$this->getData('post_data');
25
+
26
+ if (!is_object($this->options)) {
27
+ throw new Exception("Unable to get singleton: FourTell_Recommend_Model_UploadSetting");
28
+ }
29
+ $this->options->save($post);
30
+
31
+ $message = Mage::helper('adminhtml')->__('Settings have been saved successfully');
32
+ Mage::getSingleton('core/session')->addSuccess($message);
33
+
34
+ if ($post['what_data'] == "upload_data") {
35
+ if (!$this->getEnabled()) {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ if (strtolower($this->options->getSetting('upload_sales_data')) == "all") {
41
+ $result=$this->generateAllData(Mage::getStoreConfig('recommend/config/maxsalesdataageinmonths'));
42
+ } else {
43
+ $result=$this->generateAllData(1);
44
+ }
45
+ //return $result;
46
+ }
47
+
48
+ public function buildSalesFileNames()
49
+ {
50
+ $outStr = "";
51
+ if (strtolower($this->options->getSetting('upload_sales_data')) == "all") {
52
+ $numMonths = Mage::getStoreConfig('recommend/config/maxsalesdataageinmonths');
53
+ } else {
54
+ $numMonths = 1;
55
+ }
56
+
57
+ $outStr = "";
58
+
59
+ $newdate = date('Y-m-j');
60
+ $dp = explode('-', $newdate);
61
+
62
+ for ($cd = 0; $cd <= $numMonths + 1; $cd++) {
63
+ $dp = explode('-', $newdate);
64
+
65
+ $newdate = strtotime('-1 month', strtotime($newdate));
66
+ $newdate = date('Y-m-j', $newdate);
67
+
68
+ $fm = $dp[1];
69
+ $fy = $dp[0];
70
+ $fn = "sales_" . $fy . "-" . $fm . ".txt";
71
+
72
+ $this->salesFiles[$fy . "-" . $fm] = array(
73
+ 'ym' => $fy . "-" . $fm,
74
+ 'last' => ($cd == $numMonths) ? 1 : 0);
75
+
76
+ $outStr .= $fn . "\r\n";
77
+ }
78
+
79
+ return $outStr;
80
+ }
81
+
82
+ function generateAllData($howMany) {
83
+ //return $this;
84
+ require_once("FourTell/Recommend/controllers/Adminhtml/UploadformController.php");
85
+ //return $this;
86
+ $uploadModel=Mage::getSingleton('FourTell_Recommend_Adminhtml_UploadformController');
87
+ $uploadModel->__construct(Mage::app()->getRequest());
88
+ try {
89
+ $last=Mage::helper('recommend')->getLastUploaded();
90
+ //var_dump($this->options);
91
+ //exit;
92
+ $ups = $this->options->getSetting('upload_sales_data');
93
+ $upr = $this->options->getSetting('upload_replacement_data');
94
+ $upe = $this->options->getSetting('upload_exclusion_data');
95
+ $upp = $this->options->getSetting('upload_product_data');
96
+
97
+ $this->exportConfiguration();
98
+
99
+ $this->exportCategoryData();
100
+
101
+ if (strtolower($upr) != "none"){
102
+ $uploadModel->exportReplacementData();
103
+ }
104
+
105
+ if ($upe == "Yes"){
106
+ $uploadModel->exportExclusionData();
107
+ }
108
+
109
+ if ($upp == "Yes"){
110
+ if($last['last_config_catalog']+$last_config_catalog*3600<time()){
111
+ $this->exportProductData();
112
+ $last_data['last_config_catalog']=time();
113
+ }
114
+ }
115
+
116
+ if (strtolower($ups) != "none") {
117
+ if($last['last_config_sales']+$last_config_sales*3600<time()){
118
+ $this->exportSalesData();
119
+ $last_data['last_config_sales']=time();
120
+ }
121
+ } else {
122
+ $uploadModel->startGenerator();
123
+ }
124
+ //if something was uploaded, write time
125
+ if($last_data['last_config_catalog'] || $last_data['last_config_sales']){
126
+ Mage::helper('recommend')->writeLastUploaded($last_data);
127
+ }
128
+
129
+ } catch (Exception $e) {
130
+ //$this->processException($e, "generateAllData");
131
+ return $e;
132
+ }
133
+ }
134
+
135
+ function exportProductData() {
136
+ $generate = "create";
137
+
138
+ try {
139
+ // Create file header
140
+ $today = date("m-d-Y");
141
+ $this->fileContents = $this->getClientId() . "\tProductDetails.txt\t" . $generate . "\r\nVersion\t2\t" . $today . "\r\nProduct ID\tName\tAtt1 ID\tAtt2 ID\tPrice\tFilter\tLink\tImage Link\tStandard Code\r\n";
142
+
143
+ // Get all products with just data needed
144
+ $products = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect(array('sku', 'name', 'price', 'image', 'url_path', 'type_id'), 'inner')
145
+ ->joinField('stock_status', 'cataloginventory/stock_status', 'stock_status', 'product_id=entity_id', array(
146
+ 'stock_status' => Mage_CatalogInventory_Model_Stock_Status::STATUS_IN_STOCK,
147
+ //'website_id' => Mage::app()->getWebsite()->getWebsiteId(),
148
+ ));
149
+ if (!is_object($products)) {
150
+ throw new Exception("Unable to get model: catalog/product collection");
151
+ }
152
+
153
+ // Use an iterator to loop through the collection
154
+ $iter = Mage::getSingleton('core/resource_iterator');
155
+ if (!is_object($iter)) {
156
+ throw new Exception("Unable to get singleton: core/resource_iterator");
157
+ }
158
+
159
+ $iter->walk($products->getSelect(), array(array($this, 'productDetailsCallback')), array('arg1' => '====['));
160
+
161
+ // Upload ProductDetails.txt contents to server
162
+ $this->exportFile("ProductDetails.txt", $this->fileContents, "create");
163
+ } catch (Exception $e) {
164
+ //$this->processException($e, "exportProductData");
165
+ return $e;
166
+ }
167
+ }
168
+
169
+ /**
170
+ *
171
+ * This is the call back function used in the product iterator
172
+ *
173
+ */
174
+ function productDetailsCallback($args) {
175
+ try {
176
+ $product = Mage::getModel('catalog/product');
177
+ if (!is_object($product)) {
178
+ throw new Exception("Unable to get model: catalog/product");
179
+ }
180
+ $product->setData($args['row']);
181
+
182
+ $image = "";
183
+
184
+ $cat = implode(",", array_unique($product->getCategoryIds()));
185
+
186
+ //$productUrl = "";
187
+ $productUrl = $product->getProductUrl();
188
+
189
+ // Check to see if the product should be included
190
+ $incProduct = true;
191
+ //if ($product->type_id == "simple" || $product_type == "") {
192
+ if ($product->type_id == "simple" ) {
193
+ // Get product parent ids
194
+ $configurable_product_model = Mage::getModel('catalog/product_type_configurable');
195
+ if (!is_object($configurable_product_model)) {
196
+ throw new Exception("Unable to get model: catalog/product_type_configurable");
197
+ }
198
+ $parentIdArray = $configurable_product_model->getParentIdsByChild($product->getEntityId());
199
+
200
+ // Don't upload if it has parent ids
201
+ if (isset($parentIdArray[0])) {
202
+ $incProduct = false;
203
+ }
204
+ }
205
+
206
+ // Append product data to global variable: fileContents
207
+ if ($incProduct) {
208
+ if (!isset($parentIdArray[0])) {
209
+ $this->fileContents = $this->fileContents . $product->getEntityId() . "\t" . $product->getName() . "[$product->type_id]" . "\t" . $cat . "\t" . "" . "\t" . str_replace(",", "", number_format($product->getPrice(), 2)) . "\t" . "" . "\t" . $productUrl . "\t" . $image . "\t" . $product->getSku() . "\r\n";
210
+ }
211
+ }
212
+ } catch (Exception $e) {
213
+ $this->processException($e, "productDetailsCallback");
214
+ }
215
+ }
216
+
217
+ function exportConfiguration() {
218
+ $generate = "create";
219
+
220
+ try {
221
+ $today = date("m-d-Y");
222
+
223
+ if (strtolower($this->options->getSetting('upload_exclusion_data')) == "yes")
224
+ $upload_exclusion = 1;
225
+ else
226
+ $upload_exclusion = 0;
227
+
228
+ if (strtolower($this->options->getSetting('upload_replacement_data')) != "none")
229
+ $upload_replacement = 1;
230
+ else
231
+ $upload_replacement = 0;
232
+ $contents = $this->getClientId() . "\tConfigBoost.txt\t" . "create" . "\r\n" .
233
+ "Version\t" . "3" . "\r\n" .
234
+ "Owner\t" . Mage::getStoreConfig('recommend/config/owner') . "\r\n" .
235
+ "Email\t" . Mage::getStoreConfig('recommend/config/email') . "\r\n" .
236
+ "ReportLevel\t" . Mage::getStoreConfig('recommend/config/reportlevel') . "\r\n" .
237
+ "ExclusionsExist\t" . $upload_exclusion . "\r\n" .
238
+ "ReplacementsExist\t" . $upload_replacement . "\r\n" .
239
+ "MaxSalesDataAgeInMonths\t" . Mage::getStoreConfig('recommend/config/maxsalesdataageinmonths') . "\r\n";
240
+ // Upload ConfigBoost.txt contents to server
241
+ $this->exportFile("ConfigBoost.txt", $contents, "create");
242
+ } catch (Exception $e) {
243
+ //$this->processException($e, "exportConfiguration");
244
+ return $e;
245
+ }
246
+ }
247
+
248
+ function getClientId() {
249
+ return Mage::getStoreConfig('recommend/config/client_id');
250
+ }
251
+
252
+ private function draw($txt){
253
+ ob_start();
254
+ print_r($txt);
255
+ $result=ob_get_contents();
256
+ ob_end_clean();
257
+ if(!is_dir(Mage::getBaseDir('media').DS.'tmp')){
258
+ mkdir(Mage::getBaseDir('media').DS.'tmp', 0777);
259
+ }
260
+ file_put_contents(Mage::getBaseDir('media').DS.'tmp/'.date('Y_m_d_H_i_s').'.txt', $result);
261
+ }
262
+
263
+ function exportFile($fileName, $contents, $mode) {
264
+ //!!!
265
+ if (false && Mage::getStoreConfig('recommend/config/mode') == "Test" && $this->options->getSetting('showUploadDetails') == "Yes") {
266
+ try {
267
+ if ($mode == "create") {
268
+ $fmode = "w+";
269
+ } else {
270
+ $fmode = "a+";
271
+ }
272
+ if(!is_dir(Mage::getBaseDir('media').DS.'export')){
273
+ mkdir(Mage::getBaseDir('media').DS.'export', 0777);
274
+ }
275
+ $sf = fopen(Mage::getBaseDir('media').DS.'export'.DS.$fileName, $fmode);
276
+ if( $sf ) {
277
+ fwrite($sf, $contents, strlen($contents));
278
+ fclose($sf);
279
+ }
280
+ } catch (Exception $e) {
281
+ //$this->processException($e, "exportFile#1");
282
+ return $e;
283
+ }
284
+ }
285
+
286
+ try {
287
+ $mode = Mage::helper('recommend')->getMode();
288
+
289
+ // The request URL prefix
290
+ $request = "http://www.4-tell." . $mode . "/Boost2.0/rest/UploadData/stream";
291
+
292
+ $curl = curl_init($request);
293
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
294
+ curl_setopt($curl, CURLOPT_HEADER, 1);
295
+ curl_setopt($curl, CURLOPT_POST, 1);
296
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $contents);
297
+ curl_setopt($curl, CURLOPT_VERBOSE, 0);
298
+ curl_setopt($curl, CURLINFO_HEADER_OUT, 0);
299
+ $response = curl_exec($curl);
300
+ $info = curl_getinfo($curl);
301
+ if(true){
302
+ $out='request:'."\r\n".$request."\r\n\r\n";
303
+ $out.='filename:'."\r\n".$fileName."\r\n\r\n";
304
+ $out.='contents:'."\r\n".$contents."\r\n\r\n";
305
+ $out.='response:'."\r\n".$response."\r\n\r\n";
306
+ $this->draw($out);
307
+ }
308
+ if ($response === false || $info['http_code'] != 200) {
309
+ throw new Exception(curl_error($curl));
310
+ }
311
+ curl_close($curl);
312
+
313
+ if (Mage::getStoreConfig('recommend/config/mode') == "Test" && $this->options->getSetting('showUploadDetails') == "Yes") {
314
+ //remove file after uploading
315
+ if(is_dir(Mage::getBaseDir('media').DS.'export') && file_exists(Mage::getBaseDir('media').DS.'export'.DS.$fileName)){
316
+ try{
317
+ //!!!unlink(Mage::getBaseDir('media').DS.'export'.DS.$fileName);
318
+ }
319
+ catch(Exception $e){
320
+ echo $e->getMessage();
321
+ }
322
+ }
323
+ Mage::getSingleton('core/session')->addWarning("Uploading data for: " . $fileName);
324
+ }
325
+ } catch (Exception $e) {
326
+ $this->processException($e, "exportFile#2");
327
+ }
328
+ }
329
+
330
+ /*
331
+ function getMode() {
332
+ try {
333
+ if (Mage::getStoreConfig('recommend/config/mode') == "Test") {
334
+ return "biz";
335
+ }
336
+
337
+ return "net";
338
+ } catch (Exception $e) {
339
+ //$this->processException($e, "getMode");
340
+ return $e;
341
+ }
342
+ }
343
+ */
344
+
345
+ function exportCategoryData() {
346
+ $generate = "create";
347
+
348
+ try {
349
+ // Create file header
350
+ $today = date("m-d-Y");
351
+ $this->fileContents = $this->getClientId() . "\tAttribute1Names.txt\t" . $generate . "\r\nVersion\t2\t" . $today . "\r\nAttID\tName\r\n";
352
+
353
+ // Get all categories
354
+ $category = Mage::getModel('catalog/category');
355
+ if (!is_object($category)) {
356
+ throw new Exception("Unable to get model: catalog/category");
357
+ }
358
+
359
+ $collection = $category->getCollection();
360
+ if (!is_object($collection)) {
361
+ throw new Exception("Unable to get category collection");
362
+ }
363
+
364
+ $collection->addAttributeToSelect('*');
365
+
366
+ // Loop through all and add to file
367
+ foreach ($collection as $item) {
368
+ $this->fileContents = $this->fileContents . $item->getData('entity_id') . "\t" . $item->getData('name') . "\r\n";
369
+ }
370
+
371
+ // Upload Attribute1Names.txt contents to server
372
+ $this->exportFile("Attribute1Names.txt", $this->fileContents, "create");
373
+ } catch (Exception $e) {
374
+ //$this->processException($e, "exportCategoryData");
375
+ return $e;
376
+ }
377
+ }
378
+
379
+ function exportSalesData() {
380
+ try {
381
+ $newdate = date('Y-m-j');
382
+
383
+ // Generate next date string for filename
384
+ $dp = explode('-', $newdate);
385
+ $newdate = strtotime('-1 month', strtotime($newdate));
386
+ $newdate = date('Y-m-j', $newdate);
387
+
388
+ $fm = $dp[1];
389
+ $fy = $dp[0];
390
+
391
+ require_once("FourTell/Recommend/controllers/Adminhtml/UploadformController.php");
392
+ $uploadModel=Mage::getModel('FourTell_Recommend_Adminhtml_UploadformController');
393
+ $uploadModel->__construct(Mage::app()->getRequest());
394
+ $uploadModel->uploadYearMonthOrders($fy . "-" . $fm, 0);
395
+ } catch (Exception $e) {
396
+ //$this->processException($e, "exportSalesData");
397
+ return $e;
398
+ }
399
+ }
400
+
401
+
402
+
403
+ }
app/code/community/FourTell/Recommend/Model/DataProcess.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Process business logic
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ /*
14
+ class FourTell_Recommend_Model_Observer {
15
+
16
+ public function process($post) {
17
+
18
+ }
19
+
20
+ }
21
+ */
22
+ ?>
app/code/community/FourTell/Recommend/Model/Observer.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Test code for observing system events
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+
14
+ class FourTell_Recommend_Model_Observer {
15
+
16
+ function uploadData($observer) {
17
+
18
+ $last_data=array();
19
+ // If extension is not enabled then return
20
+ if (!Mage::helper('recommend')->isEnabled()){
21
+ return;
22
+ }
23
+ //check when the last time process was started
24
+ $last=Mage::helper('recommend')->getLastUploaded();
25
+ if($last===false){
26
+ $last_data['last_config_sales']=time()-3600;
27
+ $last_data['last_config_catalog']=time()-3600;
28
+ Mage::helper('recommend')->writeLastUploaded($last_data);
29
+ }
30
+ $last_config_sales=Mage::getStoreConfig('recommend/cron_jobs/sales');
31
+ if(!$last_config_sales) $last_config_sales=1;
32
+ $last_config_catalog=Mage::getStoreConfig('recommend/cron_jobs/catalog');
33
+ if(!$last_config_catalog) $last_config_catalog=1;
34
+
35
+ if($last['last_config_sales']+$last_config_sales*3600<time() || $last['last_config_catalog']+$last_config_catalog*3600<time()){
36
+ //it's time to generate a new set of data
37
+ $processModel=Mage::getModel('FourTell_Recommend_Model_Adminhtml_Uploadform');
38
+ $processModel->generateAllData(1);
39
+ //please note, 'generate all data' doesn't generate all files
40
+ //for some reasons pagination was denied for non-ajax requests
41
+ //see /app/code/community/FourTell/Recommend/controllers/Adminhtml/UploadformController.php, line 406
42
+ }
43
+ else{
44
+ //no changes needed
45
+ /*
46
+ echo ($last['last_config_sales']+$last_config_sales*3600-time());
47
+ echo '<br>';
48
+ echo ($last['last_config_catalog']+$last_config_catalog*3600-time());
49
+ echo '<br>';
50
+ */
51
+ }
52
+ }
53
+
54
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Source/Crosssell.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Generate options for Report Level selectbox in system config
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Model_System_Config_Source_Crosssell {
14
+
15
+ public function toOptionArray() {
16
+ return array(
17
+ '1' => Mage::helper('recommend')->__('Yes'),
18
+ '0' => Mage::helper('recommend')->__('No'),
19
+ );
20
+ }
21
+
22
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Source/Enabled.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Generate options for Report Level selectbox in system config
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Model_System_Config_Source_Enabled {
14
+
15
+ public function toOptionArray() {
16
+ return array(
17
+ '1' => Mage::helper('recommend')->__('Yes'),
18
+ '0' => Mage::helper('recommend')->__('No'),
19
+ );
20
+ }
21
+
22
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Source/Mode.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Generate options for Mode selectbox in system config
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Model_System_Config_Source_Mode {
14
+
15
+ public function toOptionArray() {
16
+ return array(
17
+ 'Live' => Mage::helper('recommend')->__('Live'),
18
+ 'Test' => Mage::helper('recommend')->__('Test')
19
+ );
20
+ }
21
+
22
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Source/Related.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Generate options for Report Level selectbox in system config
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Model_System_Config_Source_Related {
14
+
15
+ public function toOptionArray() {
16
+ return array(
17
+ '1' => Mage::helper('recommend')->__('Yes'),
18
+ '0' => Mage::helper('recommend')->__('No'),
19
+ );
20
+ }
21
+
22
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Source/Reportlevel.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Generate options for Report Level selectbox in system config
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Model_System_Config_Source_Reportlevel {
14
+
15
+ public function toOptionArray() {
16
+ return array(
17
+ 'Error' => Mage::helper('recommend')->__('Error'),
18
+ 'Warning' => Mage::helper('recommend')->__('Warning'),
19
+ 'Information' => Mage::helper('recommend')->__('Information'),
20
+ 'All' => Mage::helper('recommend')->__('All'),
21
+ 'None' => Mage::helper('recommend')->__('None'),
22
+ );
23
+ }
24
+
25
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Source/Resell.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Generate options for Report Level selectbox in system config
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Model_System_Config_Source_Resell {
14
+
15
+ public function toOptionArray() {
16
+ return array(
17
+ '0' => Mage::helper('recommend')->__('No'),
18
+ '1' => Mage::helper('recommend')->__('Yes'),
19
+ );
20
+ }
21
+
22
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Source/Resultformat.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Generate options for Result Format selectbox in system config
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Model_System_Config_Source_Resultformat {
14
+
15
+ public function toOptionArray() {
16
+ return array(
17
+ 'TabDelimited' => Mage::helper('recommend')->__('Tab Delimited'),
18
+ 'CommaDelimited' => Mage::helper('recommend')->__('Comma Delimited'),
19
+ 'SpaceDelimited' => Mage::helper('recommend')->__('Space Delimited'),
20
+ 'XML' => Mage::helper('recommend')->__('XML'),
21
+ );
22
+ }
23
+
24
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Source/Upsell.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * Generate options for Report Level selectbox in system config
8
+ *
9
+ * @package FourTell_Recommend
10
+ * @copyright 4-Tell, Inc.
11
+ *
12
+ */
13
+ class FourTell_Recommend_Model_System_Config_Source_Upsell {
14
+
15
+ public function toOptionArray() {
16
+ return array(
17
+ '1' => Mage::helper('recommend')->__('Yes'),
18
+ '0' => Mage::helper('recommend')->__('No'),
19
+ );
20
+ }
21
+
22
+ }
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Clientid.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Clientid extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $clientid = $this->getValue(); // get the value from our config
14
+
15
+ if (trim($clientid) == "") { // exit if we're less than 10 digits long
16
+ Mage::throwException("You must enter your Client ID.");
17
+ }
18
+
19
+ return parent::save(); // call original save method so whatever happened
20
+ // before still happens (the value saves)
21
+ }
22
+
23
+ }
24
+ ?>
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Email.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Email extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $email = $this->getValue(); // get the value from our config
14
+
15
+ if (preg_match("/^[-\w.]+@([A-z0-9][-A-z0-9]+\.)+[A-z]{2,4}$/", $email)) {
16
+
17
+ } else {
18
+ Mage::throwException("Please enter a valid email address.");
19
+ }
20
+
21
+ return parent::save(); // call original save method so whatever happened
22
+ // before still happens (the value saves)
23
+ }
24
+
25
+ }
26
+ ?>
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Maxsalesdataageinmonths.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Maxsalesdataageinmonths extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $maxsalesdataageinmonths = $this->getValue(); // get the value from our config
14
+
15
+ if (!is_numeric($maxsalesdataageinmonths) || $maxsalesdataageinmonths < 0 || $maxsalesdataageinmonths > 30) {
16
+ Mage::throwException("Max Sales Data Age In Months must not be less than 1.");
17
+ }
18
+
19
+ return parent::save(); // call original save method so whatever happened
20
+ // before still happens (the value saves)
21
+ }
22
+
23
+ }
24
+ ?>
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Mincommon.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Mincommon extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $mincommon = $this->getValue(); // get the value from our config
14
+
15
+ if (!is_numeric($mincommon) || $mincommon < 1 || $mincommon > 10) {
16
+ Mage::throwException("Min Common must be between 1 and 10.");
17
+ }
18
+
19
+ return parent::save(); // call original save method so whatever happened
20
+ // before still happens (the value saves)
21
+ }
22
+
23
+ }
24
+ ?>
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Minlikelihood.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Minlikelihood extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $minlikelihood = $this->getValue(); // get the value from our config
14
+
15
+ if (!is_numeric($minlikelihood) || $minlikelihood < 1 || $minlikelihood > 30) {
16
+ Mage::throwException("Min Likelihood must be between 1 and 30.");
17
+ }
18
+
19
+ return parent::save(); // call original save method so whatever happened
20
+ // before still happens (the value saves)
21
+ }
22
+
23
+ }
24
+ ?>
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Numcrosssell.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Numcrosssell extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $numcrosssell = $this->getValue(); // get the value from our config
14
+
15
+ if (strlen($numcrosssell) && is_numeric($numcrosssell) === false) {
16
+ Mage::throwException("Please enter a valid number of Cross Sell recommendations.");
17
+ }
18
+
19
+ return parent::save(); // call original save method so whatever happened
20
+ // before still happens (the value saves)
21
+ }
22
+
23
+ }
24
+ ?>
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Numrelated.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Numrelated extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $numrelated = $this->getValue(); // get the value from our config
14
+
15
+ if (strlen($numrelated) && is_numeric($numrelated) === false) {
16
+ Mage::throwException("Please enter a valid number of Related recommendations.");
17
+ }
18
+
19
+ return parent::save(); // call original save method so whatever happened
20
+ // before still happens (the value saves)
21
+ }
22
+
23
+ }
24
+ ?>
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Numupsell.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Numupsell extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $numupsell = $this->getValue(); // get the value from our config
14
+
15
+ if (strlen($numupsell) && is_numeric($numupsell) === false) {
16
+ Mage::throwException("Please enter a valid number of Upsell recommendations.");
17
+ }
18
+
19
+ return parent::save(); // call original save method so whatever happened
20
+ // before still happens (the value saves)
21
+ }
22
+
23
+ }
24
+ ?>
app/code/community/FourTell/Recommend/Model/System/Config/Validation/Owner.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * 4-Tell Product Recommendations
5
+ *
6
+ * @package FourTell_Recommend
7
+ * @copyright 4-Tell, Inc.
8
+ *
9
+ */
10
+ class FourTell_Recommend_Model_System_Config_Validation_Owner extends Mage_Core_Model_Config_Data {
11
+
12
+ public function save() {
13
+ $owner = $this->getValue(); // get the value from our config
14
+
15
+ if (trim($owner) == "") { // exit if we're less than 10 digits long
16
+ Mage::throwException("You must enter your owner name.");
17
+ }
18
+
19
+ return parent::save(); // call original save method so whatever happened
20
+ // before still happens (the value saves)
21
+ }
22
+
23
+ }
24
+ ?>
app/code/community/FourTell/Recommend/Model/Upload/Frequency.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class FourTell_Recommend_Model_Upload_Frequency
3
+ {
4
+ public function toOptionArray()
5
+ {
6
+ return array(
7
+ 168 => Mage::helper('adminhtml')->__('Weekly'),
8
+ 24 => Mage::helper('adminhtml')->__('Daily'),
9
+ 12 => Mage::helper('adminhtml')->__('Twice a day'),
10
+ 8 => Mage::helper('adminhtml')->__('Three times a day'),
11
+ 876000 => Mage::helper('adminhtml')->__('Disabled')
12
+ );
13
+ }
14
+ }
app/code/community/FourTell/Recommend/Model/UploadSetting.php ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * This is for loading and saving the upload configuration xml file
8
+ * settings from admin/Catalog/4-Tell Recommendations
9
+ *
10
+ * @package FourTell_Recommend
11
+ * @copyright 4-Tell, Inc.
12
+ *
13
+ */
14
+ class FourTell_Recommend_Model_UploadSetting extends Varien_Object {
15
+
16
+ const XML_PATH_UPLOAD_SETTING = 'default/recommend/upload/setting';
17
+
18
+ public $options = null;
19
+ public $upload_sales_data = "Yes";
20
+ public $upload_replacement_data = "Yes";
21
+ public $upload_product_data = "Yes";
22
+ public $upload_exclusion_data = "Yes";
23
+ public $replace_catalog_old_field = null;
24
+ public $replace_catalog_new_field = null;
25
+ public $product_attributtes = null;
26
+ public $showUploadDetails = "No";
27
+
28
+ /**
29
+ * Initialize resource model
30
+ */
31
+ protected function loadDefaultConfiguration()
32
+ {
33
+ $uploadSettingsDefault = get_object_vars(simplexml_load_file($this->getUploadOptionsPath()));
34
+
35
+ $arrSettingsDefault = $this->getSettingsDefaultArray($uploadSettingsDefault);
36
+
37
+ return $this->saveSettings(serialize($arrSettingsDefault));
38
+
39
+ }
40
+
41
+ /**
42
+ * Return an Array of Object settings.
43
+ *
44
+ * @param Mixed $data
45
+ * @return Array
46
+ */
47
+ protected function getSettingsDefaultArray($data){
48
+ if (is_object($data)) {
49
+ $arr = get_object_vars($data);
50
+ foreach ($arr as $key => $value) {
51
+ $assocArr = array();
52
+ if (is_array($value)) {
53
+ foreach ($value as $v) {
54
+ if (is_object($v) && count(get_object_vars($v))==2
55
+ && isset($v->key) && isset($v->value)) {
56
+ $assocArr[$v->key] = $v->value;
57
+ }
58
+ }
59
+ }
60
+ if (!empty($assocArr)) {
61
+ $arr[$key] = $assocArr;
62
+ }
63
+ }
64
+ $arr = $this->getSettingsDefaultArray($arr);
65
+ return $arr;
66
+ }
67
+ if (is_array($data)) {
68
+ foreach ($data as $key => $value) {
69
+ if (is_object($value) || is_array($value)) {
70
+ $data[$key] = $this->getSettingsDefaultArray($value);
71
+ } else {
72
+ $data[$key] = $value;
73
+ }
74
+ }
75
+ }
76
+ return $data;
77
+ }
78
+
79
+
80
+
81
+ public function saveSettings($settings)
82
+ {
83
+ Mage::getModel('core/config_data')
84
+ ->load(self::XML_PATH_UPLOAD_SETTING, 'path')
85
+ ->setValue($settings)
86
+ ->setPath(self::XML_PATH_UPLOAD_SETTING)
87
+ ->save();
88
+ Mage::getConfig()->cleanCache();
89
+ return $this;
90
+ }
91
+
92
+
93
+ public function getOptions() {
94
+ return $this->options;
95
+ }
96
+
97
+ public function getSetting($setting) {
98
+ $opts = $this->getOptions();
99
+
100
+ return $opts[$setting];
101
+ }
102
+
103
+ public function getExcludeOptions() {
104
+ $opts = $this->getOptions();
105
+
106
+ return $opts['exclude_options'];
107
+ }
108
+
109
+ public function setOptions($opts) {
110
+
111
+ $this->options['exclude_options'] = array();
112
+ if (isset($opts['exclude_options']['option']) && count($opts['exclude_options']['option'])) {
113
+ foreach ($opts['exclude_options']['option'] as $opt) {
114
+ $this->options['exclude_options'][] = $opt;
115
+ }
116
+ } else {
117
+ $this->options['exclude_options'] = array();
118
+ }
119
+
120
+ $this->options['replace_options'] = array();
121
+ if (isset($opts['replace_options']['option']) && count($opts['replace_options']['option'])) {
122
+ foreach ($opts['replace_options']['option'] as $opt) {
123
+ $this->options['replace_options'][] = $opt;
124
+ }
125
+ } else {
126
+ $this->options['replace_options'] = array();
127
+ }
128
+
129
+ if (isset($opts['upload_sales_data'])) {
130
+ $this->options['upload_sales_data'] = $opts['upload_sales_data'];
131
+ $this->upload_sales_data = $opts['upload_sales_data'];
132
+ } else {
133
+ $this->options['upload_sales_data'] = "Yes";
134
+ }
135
+
136
+ if (isset($opts['upload_replacement_data'])) {
137
+ $this->options['upload_replacement_data'] = $opts['upload_replacement_data'];
138
+ $this->upload_replacement_data = $opts['upload_replacement_data'];
139
+ } else {
140
+ $this->options['upload_replacement_data'] = "Yes";
141
+ }
142
+
143
+ if (isset($opts['upload_exclusion_data'])) {
144
+ $this->options['upload_exclusion_data'] = $opts['upload_exclusion_data'];
145
+ $this->upload_exclusion_data = $opts['upload_exclusion_data'];
146
+ } else {
147
+ $this->options['upload_exclusion_data'] = "Yes";
148
+ }
149
+
150
+ if (isset($opts['upload_product_data'])) {
151
+ $this->options['upload_product_data'] = $opts['upload_product_data'];
152
+ $this->upload_product_data = $opts['upload_product_data'];
153
+ } else {
154
+ $this->options['upload_product_data'] = "Yes";
155
+ }
156
+
157
+ if (isset($opts['replace_catalog_old_field'])) {
158
+ $this->options['replace_catalog_old_field'] = $opts['replace_catalog_old_field'];
159
+ $this->replace_catalog_old_field = $opts['replace_catalog_old_field'];
160
+ } else {
161
+ $this->options['replace_catalog_old_field'] = null;
162
+ }
163
+
164
+ if (isset($opts['replace_catalog_new_field'])) {
165
+ $this->options['replace_catalog_new_field'] = $opts['replace_catalog_new_field'];
166
+ $this->replace_catalog_new_field = $opts['replace_catalog_new_field'];
167
+ } else {
168
+ $this->options['replace_catalog_new_field'] = null;
169
+ }
170
+
171
+ if (isset($opts['showUploadDetails'])) {
172
+ $this->options['showUploadDetails'] = $opts['showUploadDetails'];
173
+ $this->showExportDetails = $opts['showUploadDetails'];
174
+ } else {
175
+ $this->options['showUploadDetails'] = "No";
176
+ }
177
+
178
+ }
179
+
180
+ function getUploadOptionsPath() {
181
+ Mage::Log('UploadSetting->getUploadOptionsPath');
182
+ return Mage::getModuleDir('etc', 'FourTell_Recommend') . DS . 'upload.xml';
183
+ }
184
+
185
+ public function load() {
186
+
187
+ $uploadSetting = Mage::getStoreConfig(self::XML_PATH_UPLOAD_SETTING);
188
+
189
+ if (!$uploadSetting) {
190
+ $uploadSetting = $this->loadDefaultConfiguration();
191
+ } else {
192
+ $uploadSetting = unserialize($uploadSetting);
193
+ }
194
+
195
+ $this->setOptions($uploadSetting);
196
+
197
+ return $this->getOptions();
198
+ }
199
+
200
+ public function save($post) {
201
+ $opts = array();
202
+
203
+ $ids = $this->getExcludePostIds($post);
204
+ $fields = array();
205
+ foreach ($ids as $id) {
206
+ $fields[] = $this->getExcludePostField($post, $id);
207
+ }
208
+
209
+ foreach ($fields as $field) {
210
+ $fieldNew = array();
211
+ $fieldNew['field'] = $field['Name'];
212
+ $fieldNew['compare'] = $field['Compare'];
213
+ $fieldNew['value'] = $field['Value'];
214
+ $opts['exclude_options']['option'][] = $fieldNew;
215
+ }
216
+
217
+ $ids = $this->getReplacePostIds($post);
218
+ $fields = array();
219
+ foreach ($ids as $id) {
220
+ $fields[] = $this->getReplacePostField($post, $id);
221
+ }
222
+
223
+ foreach ($fields as $field) {
224
+ $fieldNew = array();
225
+ $fieldNew['oldid'] = $field['oldid'];
226
+ $fieldNew['newid'] = $field['newid'];
227
+ $opts['replace_options']['option'][] = $fieldNew;
228
+ }
229
+
230
+ if( isset($post['upload_sales_data']) )
231
+ $opts['upload_sales_data'] = $post['upload_sales_data'];
232
+ else
233
+ $opts['upload_sales_data'] = '';
234
+
235
+ if( isset($post['upload_replacement_data']) )
236
+ $opts['upload_replacement_data'] = $post['upload_replacement_data'];
237
+ else
238
+ $opts['upload_replacement_data'] = '';
239
+
240
+ if( isset($post['upload_product_data']) )
241
+ $opts['upload_product_data'] = $post['upload_product_data'];
242
+ else
243
+ $opts['upload_product_data'] = '';
244
+
245
+ if( isset($post['upload_exclusion_data']) )
246
+ $opts['upload_exclusion_data'] = $post['upload_exclusion_data'];
247
+ else
248
+ $opts['upload_exclusion_data'] = '';
249
+
250
+ if( isset($post['replace_catalog_old_field']) )
251
+ $opts['replace_catalog_old_field'] = $post['replace_catalog_old_field'];
252
+ else
253
+ $opts['replace_catalog_old_field'] = '';
254
+
255
+ if( isset($post['replace_catalog_new_field']) )
256
+ $opts['replace_catalog_new_field'] = $post['replace_catalog_new_field'];
257
+ else
258
+ $opts['replace_catalog_new_field'] = '';
259
+
260
+ if( isset($post['showUploadDetails']) )
261
+ $opts['showUploadDetails'] = $post['showUploadDetails'];
262
+ else
263
+ $opts['showUploadDetails'] = '';
264
+
265
+ $this->setOptions ($opts);
266
+
267
+ return $this->saveSettings(serialize($opts));
268
+ }
269
+
270
+ function getExcludePostIds($post) {
271
+
272
+ $ids = array();
273
+
274
+ foreach ($post as $k => $v) {
275
+ if (strpos($k, "excludeField_Name") !== false && trim($v) != "") {
276
+ $parts = explode("_", trim($k));
277
+ $ids[] = $parts[2];
278
+ }
279
+ }
280
+
281
+ return $ids;
282
+ }
283
+
284
+ function getExcludePostField($post, $id) {
285
+
286
+ $data = array();
287
+
288
+ foreach ($post as $k => $v) {
289
+ if (strpos($k, "excludeField_") !== false && trim($v) != "") {
290
+ $parts = explode("_", trim($k));
291
+ if ($id == $parts[2]) {
292
+ $data[$parts[1]] = trim($v);
293
+ }
294
+ }
295
+ }
296
+
297
+ return $data;
298
+ }
299
+
300
+ function getReplacePostIds($post) {
301
+
302
+ $ids = array();
303
+
304
+ foreach ($post as $k => $v) {
305
+ if (strpos($k, "replaceField_oldid") !== false && trim($v) != "") {
306
+ $parts = explode("_", trim($k));
307
+ $ids[] = $parts[2];
308
+ }
309
+ }
310
+
311
+ return $ids;
312
+ }
313
+
314
+ function getReplacePostField($post, $id) {
315
+
316
+ $data = array();
317
+
318
+ foreach ($post as $k => $v) {
319
+ if (strpos($k, "replaceField_") !== false && trim($v) != "") {
320
+ $parts = explode("_", trim($k));
321
+ if ($id == $parts[2]) {
322
+ $data[$parts[1]] = trim($v);
323
+ }
324
+ }
325
+ }
326
+
327
+ return $data;
328
+ }
329
+
330
+ function _toArray() {
331
+
332
+ $data = array();
333
+
334
+ foreach ($this->getExcludeOptions()->getNode() as $opt) {
335
+ $ex = array(
336
+ 'field' => (string) $opt->field,
337
+ 'compare' => (string) $opt->compare,
338
+ 'value' => (string) $opt->value
339
+ );
340
+
341
+ $data['exclude_options'][] = $ex;
342
+ }
343
+
344
+ return $data;
345
+ }
346
+
347
+ }
348
+ ?>
app/code/community/FourTell/Recommend/controllers/Adminhtml/UploadformController.php ADDED
@@ -0,0 +1,908 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * 4-Tell Product Recommendations
6
+ *
7
+ * This is for processing the form in the admin
8
+ * Catalog/4-Tell Recommendations
9
+ *
10
+ * @package FourTell_Recommend
11
+ * @copyright 4-Tell, Inc.
12
+ *
13
+ */
14
+ class FourTell_Recommend_Adminhtml_UploadformController extends Mage_Adminhtml_Controller_Action {
15
+
16
+ /**
17
+ * Show debugging export details
18
+ *
19
+ * @var booelan
20
+ */
21
+ public $showUploadDetails = false;
22
+ /**
23
+ * Global variable for generating file contents
24
+ *
25
+ * @var string
26
+ */
27
+ public $fileContents = "";
28
+ /**
29
+ * Exclude product options
30
+ *
31
+ * @var array
32
+ */
33
+ public $excludeOptions = array();
34
+ /**
35
+ * Options for uploading data (recommend/etc/upload.xml)
36
+ *
37
+ * @var class
38
+ */
39
+ //public $uploadSettings = null;
40
+ /**
41
+ * An array for holding all the months to upload sales for
42
+ *
43
+ * @var array
44
+ */
45
+ public $salesFiles = null;
46
+ /**
47
+ * Maximum number of orders to upload at a time
48
+ *
49
+ * @var integer
50
+ */
51
+ public $maxOrders = 500;
52
+ /**
53
+ * An array for holding url parameters
54
+ *
55
+ * @var array
56
+ */
57
+ public $salesAppend = array();
58
+ /**
59
+ * An array for holding all the names of sales files to upload
60
+ *
61
+ * @var string
62
+ */
63
+ public $options = null;
64
+ /**
65
+ * An array for holding all the names of sales files to upload
66
+ *
67
+ * @var string
68
+ */
69
+ public $salesFileNames = "";
70
+ public $isAjax = false;
71
+
72
+ protected function _construct(){
73
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
74
+ $model->buildSalesFileNames();
75
+ }
76
+
77
+
78
+ /**
79
+ *
80
+ * Handler for clicking Catalog/4-Tell Recommendations
81
+ *
82
+ */
83
+ public function indexAction() {
84
+ $this->processPostAction();
85
+ //if(!$this->isAjax){
86
+ $this->loadLayout()->renderLayout();
87
+ //}
88
+ }
89
+
90
+ /**
91
+ *
92
+ * Handler for submit button on admin form
93
+ *
94
+ */
95
+ public function postAction() {
96
+ $this->processPostAction();
97
+
98
+ $this->_redirect('*/*');
99
+ }
100
+
101
+
102
+ /**
103
+ *
104
+ * Handler for submit button on admin form
105
+ *
106
+ */
107
+ public function processPostAction() {
108
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
109
+ set_time_limit(3000);
110
+
111
+ try {
112
+ $this->salesFileNames=$model->buildSalesFileNames();
113
+
114
+ $this->processGetAction();
115
+
116
+ $post = $this->getRequest()->getPost();
117
+ if (!empty($post)) {
118
+ $model->setData('post_data', $post);
119
+ $this->options=$this->getUploadSettings();
120
+ $this->options->save($post);
121
+ $resultData=$model->processData();
122
+ //if(false===$resultData) return false;
123
+ }
124
+ } catch (Exception $e) {
125
+ $this->processException($e, "processPostAction");
126
+ }
127
+ }
128
+
129
+ /**
130
+ *
131
+ * Parse the url parameters from redirections
132
+ *
133
+ * If it is a redirecton then get values and upload next sales page orders
134
+ *
135
+ * If not a redirection then set default values.
136
+ *
137
+ */
138
+ public function processGetAction() {
139
+ $params = $this->getRequest()->getParams();
140
+
141
+ if (array_key_exists('key', $params)) {
142
+ $this->salesAppend['key'] = $params['key'];
143
+ }
144
+
145
+ if (array_key_exists('isajax', $params)) {
146
+ $this->isAjax = true;
147
+ }
148
+
149
+ if (isset($params['cmd']) && isset($params['ym']) && isset($params['p'])) {
150
+ $this->salesAppend['cmd'] = $params['cmd'];
151
+ $this->salesAppend['ym'] = $params['ym'];
152
+ $this->salesAppend['p'] = intval($params['p']);
153
+
154
+ $this->showUploadProgress($this->salesAppend['ym'], $this->salesAppend['p']);
155
+
156
+ if ($this->salesAppend['cmd'] == "usf") {
157
+ $this->uploadYearMonthOrders($this->salesAppend['ym'], $this->salesAppend['p']);
158
+ }
159
+ } else if (isset($params['cmd']) && $params['cmd'] == "ucb") {
160
+ $this->exportConfiguration();
161
+ } else if (isset($params['cmd']) && $params['cmd'] == "ucd") {
162
+ $this->exportCategoryData();
163
+ } else if (isset($params['cmd']) && $params['cmd'] == "urd") {
164
+ $this->exportReplacementData();
165
+ } else if (isset($params['cmd']) && $params['cmd'] == "ued") {
166
+ $this->exportExclusionData();
167
+ } else if (isset($params['cmd']) && $params['cmd'] == "upd") {
168
+ //!!!
169
+ //$this->salesAppend['p'] = 1;
170
+ $this->exportProductData();
171
+ } else if (isset($params['cmd']) && $params['cmd'] == "gen") {
172
+ $this->startGenerator();
173
+ } else {
174
+ $this->salesAppend['cmd'] = "usf";
175
+ $this->salesAppend['ym'] = Mage::helper('recommend')->getNextUploadDate();
176
+ $this->salesAppend['p'] = 0;
177
+ }
178
+ }
179
+
180
+ /**
181
+ *
182
+ * Display a list of sales files that have been uploaded
183
+ *
184
+ */
185
+ function showUploadProgress($sf, $p) {
186
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
187
+ if(!$this->options) $this->options=$this->getUploadSettings();
188
+ try {
189
+ if (strtolower($this->options->getSetting('upload_sales_data')) != "none") {
190
+ $keys = array_keys($model->salesFiles);
191
+ $cf = $model->salesFiles[$sf];
192
+
193
+ Mage::getSingleton('core/session')->addWarning("Uploading sales orders, please wait...");
194
+
195
+ foreach ($keys as $idx => $key) {
196
+ if ($sf === $key) {
197
+ return;
198
+ }
199
+
200
+ Mage::getSingleton('core/session')->addWarning("Uploaded data for: sales_" . $key . ".txt");
201
+ }
202
+ }
203
+ } catch (Exception $e) {
204
+ $this->processException($e, "showUploadProgress");
205
+ }
206
+ }
207
+
208
+ /**
209
+ *
210
+ * Display a list of sales files that have been uploaded
211
+ *
212
+ */
213
+ function showUploadComplete() {
214
+ try {
215
+ Mage::getSingleton('core/session')->addWarning("Upload of sales orders complete.");
216
+ $this->startGenerator();
217
+ } catch (Exception $e) {
218
+ $this->processException($e, "showUploadComplete");
219
+ }
220
+ }
221
+
222
+ /**
223
+ *
224
+ * Get upload settings through the upload class
225
+ *
226
+ */
227
+ public function getUploadSettings() {
228
+ if (!$this->options) {
229
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
230
+ try {
231
+ $this->options=$model->getUploadSettings();
232
+ } catch (Exception $e) {
233
+ $this->processException($e, "getUploadSettings");
234
+ }
235
+ }
236
+ return $this->options;
237
+ }
238
+
239
+ /**
240
+ *
241
+ * Shortcut for getting admin setting of client id
242
+ *
243
+ */
244
+
245
+ function getClientId() {
246
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
247
+ try {
248
+ return $model->getClientId();
249
+ } catch (Exception $e) {
250
+ $this->processException($e, "getClientId");
251
+ }
252
+ }
253
+
254
+ /**
255
+ *
256
+ * Shortcut for getting admin setting of enabled
257
+ *
258
+ */
259
+ function getEnabled() {
260
+ try {
261
+ return intval(Mage::getStoreConfig('recommend/config/enabled'));
262
+ } catch (Exception $e) {
263
+ $this->processException($e, "getEnabled");
264
+ }
265
+ }
266
+
267
+ /**
268
+ *
269
+ * Generate and upload config settings
270
+ *
271
+ */
272
+
273
+ function exportConfiguration() {
274
+ try {
275
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
276
+ $model->exportConfiguration();
277
+ } catch (Exception $e) {
278
+ $this->processException($e, "exportConfiguration");
279
+ }
280
+ }
281
+
282
+ /**
283
+ *
284
+ * Generate and upload category data
285
+ *
286
+ */
287
+
288
+ function exportCategoryData() {
289
+ try {
290
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
291
+ $model->exportCategoryData();
292
+ } catch (Exception $e) {
293
+ $this->processException($e, "exportCategoryData");
294
+ }
295
+ }
296
+
297
+ /**
298
+ *
299
+ * Generate sale data for a specified month and export it
300
+ *
301
+ */
302
+ function uploadYearMonthOrders($sf, $p) {
303
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
304
+ try {
305
+ $generate = false;
306
+
307
+ $fn = "sales_" . $sf . ".txt";
308
+ $sp = explode("-", $sf);
309
+
310
+ $cf = $model->salesFiles[$sf];
311
+ $isLastUploadMonth = $cf['last'];
312
+ //$p=1;//!!!
313
+ if ($p == 0) {
314
+ $type = "create";
315
+ } else {
316
+ $type = "append";
317
+ }
318
+
319
+ $items = null;
320
+ $item = null;
321
+ $data = null;
322
+ $configurable_product_model = Mage::getModel('catalog/product_type_configurable');
323
+
324
+ // Get the sales data for the month as a collection
325
+ $collection = $this->getSalesOrdersForMonth($sp[0], $sp[1], $p);
326
+
327
+ if ($collection->count() < $this->maxOrders && $isLastUploadMonth == 1) {
328
+ $completed = "uploadComplete";
329
+ } else if ($collection->count() < $this->maxOrders) {
330
+ $completed = "monthComplete";
331
+ } else {
332
+ $completed = "false";
333
+ }
334
+
335
+ $today = date("m-d-Y");
336
+ unset($contents);
337
+
338
+ if ($type == "create") {
339
+ $contents = $this->getClientId() . "\t" . $fn . "\t" . $type . "\r\nVersion\t2\t" . $today . "\r\nProduct ID\tCustomer ID\tQuantity\tDate\r\n";
340
+ } else {
341
+ $contents = $this->getClientId() . "\t" . $fn . "\t" . $type . "\r\n";
342
+ }
343
+
344
+ // Loop through the collection data
345
+ foreach ($collection as $order) {
346
+ // Get customer ID of use who placed the order
347
+ // if the customer was an account
348
+ $customerId = $order->getData('customer_id');
349
+ if ($customerId == '') {
350
+ // Get email of use who placed the order if
351
+ // the order was placed with guest checkout
352
+ $customerId = $order->getData('customer_email');
353
+ if ($customerId == '') {
354
+ // Get order ID as last resort to use
355
+ // as the customer ID
356
+ $customerId = $order->getData('increment_id');
357
+ } else {
358
+ // If we ended up with an email address then
359
+ // make into a hash so we are not transmitting
360
+ // or storing readable personal information
361
+ $customerId = md5($customerId);
362
+ }
363
+ }
364
+
365
+ // Get all the order items for the current order
366
+ $items = $order->getAllItems();
367
+
368
+ // Loop through the order items
369
+ foreach ($items as $item) {
370
+ // Get the data for the current item
371
+ $product_id = $item->getData('product_id');
372
+ $product_type = $item->getData('product_type');
373
+ $created_at = $item->getData('created_at');
374
+ $qty_ordered = $item->getData('qty_ordered');
375
+ $created_at = $order->getData('created_at');
376
+
377
+ // If the item in not configurable then proceed
378
+ if ($product_type != "configurable") {
379
+ $dt = new DateTime($created_at);
380
+
381
+ if ($product_type == "simple" || $product_type == "") {
382
+ //$configurable_product_model = Mage::getModel('catalog/product_type_configurable');
383
+ if (!is_object($configurable_product_model)) {
384
+ throw new Exception("Unable to get model: catalog/product_type_configurable");
385
+ }
386
+
387
+ $parentIdArray = $configurable_product_model->getParentIdsByChild($product_id);
388
+ if (isset($parentIdArray[0])) {
389
+ $product_id = $parentIdArray[0];
390
+ }
391
+ }
392
+
393
+ $contents = $contents . $product_id . "\t" . $customerId . "\t" . intval($qty_ordered) . "\t" . $dt->format('Y-m-d') . "\r\n";
394
+ }
395
+
396
+ unset($item);
397
+ unset($data);
398
+ }
399
+ }
400
+
401
+ unset($collection);
402
+
403
+ $model->exportFile($fn, $contents, $type);
404
+
405
+
406
+ if ($this->isAjax) {
407
+ $msg = array(
408
+ 'status' => $completed,
409
+ 'month' => "",
410
+ 'page' => ""
411
+ );
412
+
413
+ if ($completed != "uploadComplete") {
414
+ if ($completed == "monthComplete") {
415
+ $month = Mage::helper('recommend')->getNextUploadDate($sf);
416
+ $page = 0;
417
+ $msg['month'] = $month;
418
+ $msg['page'] = $page;
419
+ } else {
420
+ $page = $p + 1;
421
+ $msg['month'] = $sf;
422
+ $msg['page'] = $page;
423
+ }
424
+ }
425
+ $this->getResponse()->setBody(Zend_Json::encode($msg));
426
+ }
427
+
428
+
429
+ if ($completed != "uploadComplete") {
430
+ $this->processNextSales($sf, $p, $completed);
431
+ } else {
432
+ $this->showUploadComplete();
433
+ }
434
+ } catch (Exception $e) {
435
+ $this->processException($e, "uploadYearMonthOrders");
436
+ }
437
+ }
438
+
439
+ public function processNextSales($sf, $p, $completed){
440
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
441
+ /*
442
+ require_once("FourTell/Recommend/controllers/Adminhtml/UploadformController.php");
443
+ $uploadModel=Mage::getSingleton('FourTell_Recommend_Adminhtml_UploadformController');
444
+ $uploadModel->__construct(Mage::app()->getRequest());
445
+ */
446
+ //move back to controller!
447
+ $cf = $model->salesFiles[$sf];
448
+ $isLastUploadMonth = $cf['last'];
449
+
450
+ if ($completed == "monthComplete") {
451
+ $this->salesAppend['ym'] = Mage::helper('recommend')->getNextUploadDate($sf);
452
+ $this->salesAppend['p'] = 0;
453
+ } else {
454
+ $this->salesAppend['p']++;
455
+ }
456
+ /*
457
+ $newUrl = Mage::getBaseUrl() .
458
+ Mage::app()->getRequest()->getModuleName() .
459
+ "/" . Mage::app()->getRequest()->getControllerName() .
460
+ "/index";
461
+ */
462
+ $newUrl = Mage::getUrl('*/*/index');
463
+
464
+ if (array_key_exists('key', $this->salesAppend) && !empty($this->salesAppend['key'])) {
465
+ $newUrl .= "/key/" . $this->salesAppend['key'];
466
+ }
467
+ elseif($admin_html_key=Mage::getSingleton('adminhtml/url')->getSecretKey("adminhtml_uploadform","index")){
468
+ $newUrl .= "key/" . $admin_html_key;
469
+ }
470
+ $newUrl .=
471
+ "/cmd/" . $this->salesAppend['cmd'] .
472
+ "/ym/" . $this->salesAppend['ym'] .
473
+ "/p/" . $this->salesAppend['p'];
474
+
475
+ $jsUrl = '<script type="text/javascript">window.location.href="' . $newUrl . '";</script>';
476
+ //$curl = curl_init($newUrl);
477
+ //$response = curl_exec($curl);
478
+ //curl_close($curl);
479
+ Mage::getSingleton('core/session')->addWarning($jsUrl);
480
+ }
481
+
482
+ /**
483
+ *
484
+ * After uploading a sales files this gets called, this determines if we
485
+ * should go to the next page of ther current sales file or go to the next
486
+ * sales file or if we have finished uploading all sales data
487
+ *
488
+ */
489
+ /*
490
+ function processNextSales($sf, $p, $completed) {
491
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
492
+ $cf = $model->salesFiles[$sf];
493
+ $isLastUploadMonth = $cf['last'];
494
+
495
+ if ($completed == "monthComplete") {
496
+ $this->salesAppend['ym'] = $this->getNextUploadDate($sf);
497
+ $this->salesAppend['p'] = 0;
498
+ } else {
499
+ $this->salesAppend['p']++;
500
+ }
501
+
502
+ $newUrl = Mage::getBaseUrl() .
503
+ $this->getRequest()->getModuleName() .
504
+ "/" . $this->getRequest()->getControllerName() .
505
+ "/index";
506
+
507
+ if (array_key_exists('key', $this->salesAppend) && !empty($this->salesAppend['key'])) {
508
+ $newUrl .= "/key/" . $this->salesAppend['key'];
509
+ }
510
+
511
+ $newUrl .=
512
+ "/cmd/" . $this->salesAppend['cmd'] .
513
+ "/ym/" . $this->salesAppend['ym'] .
514
+ "/p/" . $this->salesAppend['p'];
515
+
516
+ $jsUrl = '<script type="text/javascript">window.location.href="' . $newUrl . '";</script>';
517
+ Mage::getSingleton('core/session')->addWarning($jsUrl);
518
+ }
519
+ */
520
+ /**
521
+ *
522
+ * Check to see of the current sales file is the last sales file in the
523
+ * the array of sales file to upload
524
+ *
525
+ */
526
+ function checkIsLastUploadMonth($sf) {
527
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
528
+ $ret = 0;
529
+ $keys = array_keys($model->salesFiles);
530
+
531
+ foreach ($keys as $idx => $key) {
532
+ if ($sf === $key && $idx == count($keys)) {
533
+ $ret = 1;
534
+ }
535
+ }
536
+
537
+ return $ret;
538
+ }
539
+
540
+ /**
541
+ *
542
+ * Get the next sales file from the array of sales files, if parameter
543
+ * is null then we return the first sales files, allowing null is used
544
+ * for when we do the first redirection sine we won't have a value at
545
+ * that pint to pass in
546
+ *
547
+ */
548
+ /*
549
+ function getNextUploadDate($sf = null) {
550
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
551
+ $ret = 0;
552
+ $keys = array_keys($model->salesFiles);
553
+
554
+ $getNext = 0;
555
+ if ($sf == null) {
556
+ $getNext = 1;
557
+ }
558
+
559
+ foreach ($keys as $idx => $key) {
560
+ if ($getNext == 1) {
561
+ return $key;
562
+ }
563
+
564
+ if ($sf === $key) {
565
+ $getNext = 1;
566
+ }
567
+ }
568
+
569
+ return null;
570
+ }
571
+ */
572
+ /**
573
+ *
574
+ * Get the sales data for the specified month and year
575
+ *
576
+ */
577
+ function getSalesOrdersForMonth($year, $month, $p) {
578
+ $collection = null;
579
+
580
+ try {
581
+ $orders = Mage::getModel('sales/order');
582
+ if (!is_object($orders)) {
583
+ throw new Exception("Unable to get model: sales/order");
584
+ }
585
+
586
+ $collection = $orders->getCollection();
587
+ if (!is_object($collection)) {
588
+ throw new Exception("Unable to get order collection");
589
+ }
590
+
591
+ $collection->addFieldToFilter('created_at', array('gt' => $year . '-' . $month . '-01'));
592
+ $collection->addFieldToFilter('created_at', array('lt' => $year . '-' . ($month + 1) . '-01'));
593
+ $collection->setPageSize($this->maxOrders);
594
+ $collection->setCurPage($p);
595
+ } catch (Exception $e) {
596
+ $this->processException($e, "exportSalesData");
597
+ }
598
+
599
+ $startPos = ($p * $this->maxOrders) + 1;
600
+ Mage::log("Year: " . $year . "] Month: " . $month . "] Start: " . $startPos . "] Max: " . $this->maxOrders . "] Page: " . $p . "] Count: " . $collection->count());
601
+
602
+ return $collection;
603
+ }
604
+
605
+ /**
606
+ *
607
+ * Get the number of orders for the specified month and year
608
+ *
609
+ */
610
+ function getSalesCountForMonth($year, $month) {
611
+ try {
612
+ $from = $year . "-" . intval($month) . "-1 00:00:00";
613
+ $to = $year . "-" . ($month + 1) . "-1 00:00:00";
614
+
615
+ $_collection = Mage::getResourceModel('sales/order_collection')->addAttributeToSelect('*')->addAttributeToFilter('created_at', array('from' => $from, 'to' => $to));
616
+ } catch (Exception $e) {
617
+ $processException($e, "getSalesCountForMonth");
618
+ return 0;
619
+ }
620
+
621
+ return $_collection->count();
622
+ }
623
+
624
+ /**
625
+ *
626
+ * Export Product Data
627
+ *
628
+ */
629
+
630
+ function exportProductData() {
631
+ try {
632
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
633
+ $model->exportProductData();
634
+ } catch (Exception $e) {
635
+ $this->processException($e, "exportProductData");
636
+ }
637
+ }
638
+
639
+ /**
640
+ *
641
+ * Export Product Replacement Data
642
+ *
643
+ */
644
+ function exportReplacementData() {
645
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
646
+ if(!$this->options){
647
+ $this->getUploadSettings();
648
+ }
649
+ $generate = "false";
650
+
651
+ try {
652
+ // Create file header
653
+ $today = date("m-d-Y");
654
+ $this->fileContents = $this->getClientId() . "\tReplacements.txt\t" . $generate . "\r\nVersion\t2\t" . $today . "\r\nOld Product ID\tNew Product ID\r\n";
655
+
656
+ if ($this->options->getSetting('upload_replacement_data') == "Entire Catalog") {
657
+ // Get all products with just data needed
658
+ $products = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect($this->options->getSetting('replace_catalog_old_field'), $this->options->getSetting('replace_catalog_new_field'))->addAttributeToSelect(array('name', 'price', 'image', 'url_path'), 'inner');
659
+
660
+ // Use an iterator to loop through the collection
661
+ Mage::getSingleton('core/resource_iterator')->walk($products->getSelect(), array(array($this, 'replacementCallback')), array('arg1' => '====['));
662
+ } else if ($this->options->getSetting('upload_replacement_data') == "Individual Items") {
663
+ $opts = Mage::getSingleton('FourTell_Recommend_Model_UploadSetting')->load();
664
+
665
+ foreach ($opts['replace_options'] as $o) {
666
+ $this->fileContents = $this->fileContents . (string) $o->oldid . "\t" . (string) $o->newid . "\r\n";
667
+ }
668
+ }
669
+
670
+ // Upload Replacements.txt contents to server
671
+ $model->exportFile("Replacements.txt", $this->fileContents, "create");
672
+ } catch (Exception $e) {
673
+ $this->processException($e, "exportReplacementData");
674
+ }
675
+ }
676
+
677
+ /**
678
+ *
679
+ * This is the call back function used in the replacement iterator
680
+ *
681
+ */
682
+ function replacementCallback($args) {
683
+ try {
684
+ $product = Mage::getModel('catalog/product');
685
+ $product->setData($args['row']);
686
+
687
+ $incProduct = true;
688
+ if ($product->type_id == "simple" || $product_type == "") {
689
+ // Get product parent ids
690
+ $configurable_product_model = Mage::getModel('catalog/product_type_configurable');
691
+ if (!is_object($configurable_product_model)) {
692
+ throw new Exception("Unable to get model: catalog/product_type_configurable");
693
+ }
694
+ $parentIdArray = $configurable_product_model->getParentIdsByChild($product->getEntityId());
695
+
696
+ // Don't upload if it has parent ids
697
+ if (isset($parentIdArray[0])) {
698
+ $dataProduct = Mage::getModel('catalog/product')->load($parentIdArray[0]);
699
+ } else {
700
+ $dataProduct = $product;
701
+ }
702
+ } else {
703
+ $dataProduct = $product;
704
+ }
705
+
706
+ $this->fileContents = $this->fileContents . $product->getData($this->options->getSetting('replace_catalog_old_field')) . "\t" . $dataProduct->getData($this->options->getSetting('replace_catalog_new_field')) . "\r\n";
707
+ } catch (Exception $e) {
708
+ $this->processException($e, "replacementCallback");
709
+ }
710
+ }
711
+
712
+ /**
713
+ *
714
+ * Export Product Exclusion Data
715
+ *
716
+ */
717
+ function exportExclusionData() {
718
+ $model=Mage::getSingleton('FourTell_Recommend_Model_Adminhtml_Uploadform');
719
+ $generate = "false";
720
+ try {
721
+ $opts = Mage::getSingleton('FourTell_Recommend_Model_UploadSetting')->load();
722
+
723
+ if (count($opts['exclude_options'])) {
724
+ // Create file header
725
+ $today = date("m-d-Y");
726
+ $this->fileContents = $this->getClientId() . "\tDoNotRecommend.txt\t" . $generate . "\r\nVersion\t2\t" . $today . "\r\nProduct ID\r\n";
727
+
728
+ // Create array of filter options
729
+ $filters = array();
730
+ foreach ($opts['exclude_options'] as $o) {
731
+ $filt = array();
732
+ $filt['attribute'] = (string) $o['field'];
733
+ $filt[(string) $o['compare']] = (string) $o['value'];
734
+
735
+ $filters[] = $filt;
736
+ }
737
+
738
+ // Get product collection based on filter options
739
+ $products = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect('sku')->addFieldToFilter($filters);
740
+
741
+ // Use an iterator to loop through the collection
742
+ Mage::getSingleton('core/resource_iterator')->walk($products->getSelect(), array(array($this, 'exclusionCallback')), array('arg1' => '====['));
743
+
744
+ // Upload DoNotRecommend.txt contents to server
745
+ $model->exportFile("DoNotRecommend.txt", $this->fileContents, "create");
746
+ }
747
+ } catch (Exception $e) {
748
+ $this->processException($e, "exportExclusionData");
749
+ }
750
+ }
751
+
752
+ /**
753
+ *
754
+ * This is the call back function used in the exclusion iterator
755
+ *
756
+ */
757
+ function exclusionCallback($args) {
758
+ try {
759
+ $product = Mage::getModel('catalog/product');
760
+ $product->setData($args['row']);
761
+
762
+ $this->fileContents = $this->fileContents . $product->getEntityId() . "\r\n";
763
+ } catch (Exception $e) {
764
+ $this->processException($e, "exclusionCallback");
765
+ }
766
+ }
767
+
768
+ /**
769
+ *
770
+ * Get mode (live or test)
771
+ *
772
+ */
773
+ /*
774
+ function getMode() {
775
+ try {
776
+ if (Mage::getStoreConfig('recommend/config/mode') == "Test") {
777
+ return "biz";
778
+ }
779
+
780
+ return "net";
781
+ } catch (Exception $e) {
782
+ $this->processException($e, "getMode");
783
+ }
784
+ }
785
+ */
786
+ /**
787
+ *
788
+ * Upload data to 4-Tell server
789
+ *
790
+ */
791
+
792
+ /**
793
+ *
794
+ * Upload data to 4-Tell server
795
+ *
796
+ */
797
+ function startGenerator() {
798
+ try {
799
+ $mode = Mage::helper('recommend')->getMode();
800
+
801
+ // The request URL prefix
802
+ $request = "http://www.4-tell." . $mode . "/Boost2.0/rest/GenerateDataTables?clientAlias=" . $this->getClientId() . "&reloadTables=true";
803
+
804
+ $fields = array(
805
+ 'clientAlias' => urlencode($this->getClientId()),
806
+ 'reloadTables' => urlencode("true")
807
+ );
808
+
809
+ $fields_string = "";
810
+ //url-ify the data for the POST
811
+ foreach ($fields as $key => $value) {
812
+ $fields_string .= $key . '=' . $value . '&';
813
+ }
814
+ rtrim($fields_string, '&');
815
+
816
+ $session = curl_init($request);
817
+
818
+ curl_setopt($session, CURLOPT_RETURNTRANSFER, 1);
819
+ curl_setopt($session, CURLOPT_HEADER, 1);
820
+ curl_setopt($session, CURLOPT_POST, 1);
821
+ curl_setopt($session, CURLOPT_POSTFIELDS, $fields_string);
822
+ curl_setopt($session, CURLOPT_VERBOSE, 0);
823
+ curl_setopt($session, CURLINFO_HEADER_OUT, 0);
824
+
825
+ $response = curl_exec($session);
826
+ if ($response === false) {
827
+ throw new Exception(curl_error($session));
828
+ }
829
+ curl_close($session);
830
+
831
+ if (stripos($response, "200 OK") === false) {
832
+ throw new Exception("Error generating data: " . ": " . $response);
833
+ Mage::getSingleton('core/session')->addError("Generator reload failed");
834
+
835
+ return false;
836
+ } else {
837
+ if (Mage::getStoreConfig('recommend/config/mode') == "Test" && $this->options->getSetting('showUploadDetails') == "Yes") {
838
+ Mage::getSingleton('core/session')->addWarning("Generator successful, data table reload queued");
839
+ }
840
+
841
+ return true;
842
+ }
843
+ } catch (Exception $e) {
844
+ $this->processException($e, "exportFile");
845
+
846
+ return false;
847
+ }
848
+ }
849
+
850
+ /**
851
+ *
852
+ * Add error message to browser and log error file if logging is turned on
853
+ *
854
+ * @param e Exception object
855
+ * @param function Name of function where exception occurred
856
+ */
857
+ function processException($e, $function) {
858
+ Mage::getSingleton('core/session')->addError($e->getMessage());
859
+ Mage::log("4-Tell (UploadformController::" . $function . ") :: " . $e->getMessage(), null, "4tell.log");
860
+ }
861
+
862
+ /**
863
+ *
864
+ * Test code for getting all available product attributes
865
+ *
866
+ * @return array a list of available product attributes
867
+ *
868
+ */
869
+ public function getProductAttributes() {
870
+ $prodAttrs = array();
871
+
872
+ try {
873
+ $attrs = Mage::getModel("Mage_Eav_Model_Entity_Attribute");
874
+ if (!is_object($attrs)) {
875
+ throw new Exception("Unable to get model: Mage_Eav_Model_Entity_Attribute");
876
+ }
877
+
878
+ $sets = Mage::getModel("Mage_Eav_Model_Entity_Attribute_Set");
879
+ if (!is_object($sets)) {
880
+ throw new Exception("Unable to get model: Mage_Eav_Model_Entity_Attribute_Set");
881
+ }
882
+
883
+ foreach ($attrs->getCollection() as $attr) {
884
+ if ($attr->getData("entity_type_id") == 10)
885
+ $prodAttrs[] = array('name' => $attr->getData("attribute_code"), 'label' => $attr->getData("frontend_label"));
886
+ }
887
+
888
+ usort($prodAttrs, array(Mage::helper('recommend'), 'compare_type'));
889
+ } catch (Exception $e) {
890
+ $this->processException($e, "getProductAttributes");
891
+ }
892
+
893
+ return $prodAttrs;
894
+ }
895
+
896
+ /**
897
+ * Compare types $a and $b
898
+ *
899
+ * @param array $a
900
+ * @param array $b
901
+ * @return numeric
902
+ */
903
+ /*
904
+ private function compare_type($a, $b) {
905
+ return strnatcmp($a['name'], $b['name']);
906
+ }
907
+ */
908
+ }
app/code/community/FourTell/Recommend/etc/config.xml ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <FourTell_Recommend>
5
+ <version>1.2.8</version>
6
+ </FourTell_Recommend>
7
+ </modules>
8
+
9
+ <global>
10
+ <models>
11
+ <recommend>
12
+ <class>FourTell_Recommend_Model</class>
13
+ <resourceModel>recommend_mysql4</resourceModel>
14
+ </recommend>
15
+
16
+ <recommend_mysql4>
17
+ <class>FourTell_Recommend_Model_Mysql4</class>
18
+ </recommend_mysql4>
19
+ </models>
20
+
21
+ <blocks>
22
+ <recommend>
23
+ <class>FourTell_Recommend_Block</class>
24
+ </recommend>
25
+
26
+ <!-- Community Edition -->
27
+ <catalog>
28
+ <rewrite>
29
+ <product_list_upsell>FourTell_Recommend_Block_Catalog_Product_List_Upsell</product_list_upsell>
30
+ </rewrite>
31
+ </catalog>
32
+
33
+ <checkout>
34
+ <rewrite>
35
+ <cart_crosssell>FourTell_Recommend_Block_Checkout_Cart_Crosssellcommunity</cart_crosssell>
36
+ </rewrite>
37
+ </checkout>
38
+
39
+ <!-- Enterprise Edition -->
40
+ <enterprise_targetrule>
41
+ <rewrite>
42
+ <catalog_product_list_related>FourTell_Recommend_Block_Catalog_Product_List_Related</catalog_product_list_related>
43
+ <catalog_product_list_upsell>FourTell_Recommend_Block_Catalog_Product_List_Upsell</catalog_product_list_upsell>
44
+ <checkout_cart_crosssell>FourTell_Recommend_Block_Checkout_Cart_Crosssellenterprise</checkout_cart_crosssell>
45
+ </rewrite>
46
+ </enterprise_targetrule>
47
+ </blocks>
48
+
49
+ <helpers>
50
+ <recommend>
51
+ <class>FourTell_Recommend_Helper</class>
52
+ </recommend>
53
+ </helpers>
54
+ </global>
55
+
56
+ <admin>
57
+ <routers>
58
+ <recommend>
59
+ <use>admin</use>
60
+ <args>
61
+ <module>FourTell_Recommend</module>
62
+ <frontName>recommend</frontName>
63
+ </args>
64
+ </recommend>
65
+ </routers>
66
+ </admin>
67
+
68
+ <frontend>
69
+ <layout>
70
+ <updates>
71
+ <recommend>
72
+ <file>recommend.xml</file>
73
+ </recommend>
74
+ </updates>
75
+ </layout>
76
+ </frontend>
77
+
78
+ <adminhtml>
79
+ <translate>
80
+ <modules>
81
+ <mage_adminhtml>
82
+ <files>
83
+ <recommend>FourTell_Recommend.csv</recommend>
84
+ </files>
85
+ </mage_adminhtml>
86
+ </modules>
87
+ </translate>
88
+
89
+ <menu>
90
+ <catalog>
91
+ <children>
92
+ <recommend_adminform translate="title" module="recommend">
93
+ <title>4-Tell Recommendations</title>
94
+ <action>recommend/adminhtml_uploadform</action>
95
+ </recommend_adminform>
96
+ </children>
97
+ </catalog>
98
+ </menu>
99
+
100
+ <acl>
101
+ <resources>
102
+ <admin>
103
+ <children>
104
+ <catalog>
105
+ <children>
106
+ <recommend_adminform>
107
+ <title>4-Tell Recommendations</title>
108
+ </recommend_adminform>
109
+ </children>
110
+ </catalog>
111
+ </children>
112
+ </admin>
113
+ </resources>
114
+ </acl>
115
+
116
+ <acl>
117
+ <resources>
118
+ <admin>
119
+ <children>
120
+ <system>
121
+ <children>
122
+ <config>
123
+ <children>
124
+ <recommend>
125
+ <title>4-Tell Recommendation Service</title>
126
+ </recommend>
127
+ </children>
128
+ </config>
129
+ </children>
130
+ </system>
131
+ </children>
132
+ </admin>
133
+ </resources>
134
+ </acl>
135
+
136
+ <layout>
137
+ <updates>
138
+ <recommend>
139
+ <file>recommend.xml</file>
140
+ </recommend>
141
+ </updates>
142
+ </layout>
143
+ </adminhtml>
144
+
145
+ <crontab>
146
+ <jobs>
147
+ <recommend_uploader>
148
+ <schedule><cron_expr>* * * * *</cron_expr></schedule>
149
+ <run><model>recommend/observer::uploadData</model></run>
150
+ </recommend_uploader>
151
+ </jobs>
152
+ </crontab>
153
+
154
+ <default>
155
+ <recommend>
156
+ <config>
157
+ <enabled>1</enabled>
158
+ <extensionversion>1.2.8</extensionversion>
159
+ <minlikelihood>3</minlikelihood>
160
+ <mincommon>2</mincommon>
161
+ <maxsalesdataageinmonths>18</maxsalesdataageinmonths>
162
+ </config>
163
+
164
+ <display_recommendation>
165
+ <numupsell>3</numupsell>
166
+ <numcrosssell>3</numcrosssell>
167
+ <numrelated>3</numrelated>
168
+ </display_recommendation>
169
+ </recommend>
170
+ </default>
171
+
172
+ </config>
app/code/community/FourTell/Recommend/etc/system.xml ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <recommend translate="label" module="recommend">
5
+ <label>4-Tell Recommendation Service</label>
6
+ <tab>service</tab>
7
+ <frontend_type>text</frontend_type>
8
+ <sort_order>999</sort_order>
9
+ <show_in_default>1</show_in_default>
10
+ <show_in_website>1</show_in_website>
11
+ <show_in_store>1</show_in_store>
12
+ <groups>
13
+ <config translate="label">
14
+ <label>General Settings</label>
15
+ <comment>If you haven't already signed up for your Client Alias, please &lt;a target="_blank" href="http://www.4-tell.com/signup"&gt;sign up now&lt;/a&gt;!</comment>
16
+ <frontend_type>text</frontend_type>
17
+ <sort_order>10</sort_order>
18
+ <show_in_default>1</show_in_default>
19
+ <show_in_website>1</show_in_website>
20
+ <show_in_store>1</show_in_store>
21
+ <fields>
22
+ <upload translate="label tooltip">
23
+ <label>Data Upload/Settings</label>
24
+ <frontend_model>recommend/system_config_form_field_upload</frontend_model>
25
+ <sort_order>1</sort_order>
26
+ <show_in_default>1</show_in_default>
27
+ <show_in_website>1</show_in_website>
28
+ <show_in_store>1</show_in_store>
29
+ <tooltip>Click to configure upload settings and upload data</tooltip>
30
+ </upload>
31
+
32
+ <enabled translate="label tooltip">
33
+ <label>Enabled</label>
34
+ <frontend_type>select</frontend_type>
35
+ <source_model>recommend/system_config_source_enabled</source_model>
36
+ <backend_model>recommend/system_config_validation_clientid</backend_model>
37
+ <sort_order>3</sort_order>
38
+ <show_in_default>1</show_in_default>
39
+ <show_in_website>1</show_in_website>
40
+ <show_in_store>1</show_in_store>
41
+ <tooltip>Select yes to enable the 4-Tell extension or no disabled the extension.</tooltip>
42
+ </enabled>
43
+
44
+ <extensionversion translate="label tooltip">
45
+ <label>Extension Version</label>
46
+ <frontend_type>label</frontend_type>
47
+ <sort_order>5</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>1</show_in_website>
50
+ <show_in_store>1</show_in_store>
51
+ <tooltip>The version number of the 4-Tell extension</tooltip>
52
+ </extensionversion>
53
+
54
+ <client_id translate="label tooltip">
55
+ <label>Client ID</label>
56
+ <frontend_type>text</frontend_type>
57
+ <backend_model>recommend/system_config_validation_clientid</backend_model>
58
+ <sort_order>10</sort_order>
59
+ <show_in_default>1</show_in_default>
60
+ <show_in_website>1</show_in_website>
61
+ <show_in_store>1</show_in_store>
62
+ <tooltip>Enter your Client ID, this is supplied to you by 4-Tell.</tooltip>
63
+ </client_id>
64
+
65
+ <owner translate="label tooltip">
66
+ <label>Owner</label>
67
+ <frontend_type>text</frontend_type>
68
+ <backend_model>recommend/system_config_validation_owner</backend_model>
69
+ <sort_order>20</sort_order>
70
+ <show_in_default>1</show_in_default>
71
+ <show_in_website>1</show_in_website>
72
+ <show_in_store>1</show_in_store>
73
+ <tooltip>Name of contact point (POC) for errors and issues.</tooltip>
74
+ </owner>
75
+
76
+ <email translate="label tooltip">
77
+ <label>Email</label>
78
+ <frontend_type>text</frontend_type>
79
+ <backend_model>recommend/system_config_validation_email</backend_model>
80
+ <sort_order>30</sort_order>
81
+ <show_in_default>1</show_in_default>
82
+ <show_in_website>1</show_in_website>
83
+ <show_in_store>1</show_in_store>
84
+ <tooltip>Email address for the POC.</tooltip>
85
+ </email>
86
+
87
+ <reportlevel translate="label tooltip">
88
+ <label>Report Level</label>
89
+ <frontend_type>select</frontend_type>
90
+ <source_model>recommend/system_config_source_reportlevel</source_model>
91
+ <sort_order>40</sort_order>
92
+ <show_in_default>1</show_in_default>
93
+ <show_in_website>1</show_in_website>
94
+ <show_in_store>1</show_in_store>
95
+ <tooltip>Types of messages (errors, warnings, etc.) that should be sent to the POC. The options (in decreasing order) are None, Error, Warning, Information, All. The default value is None. If a different level is chosen, all messages generated for your Client Alias at that level or higher will be emailed to the POC.</tooltip>
96
+ </reportlevel>
97
+
98
+ <resell translate="label tooltip">
99
+ <label>Resell</label>
100
+ <frontend_type>select</frontend_type>
101
+ <source_model>recommend/system_config_source_resell</source_model>
102
+ <sort_order>80</sort_order>
103
+ <show_in_default>1</show_in_default>
104
+ <show_in_website>1</show_in_website>
105
+ <show_in_store>1</show_in_store>
106
+ <tooltip>Whether previously purchased products should be offered to the same customer.</tooltip>
107
+ </resell>
108
+
109
+ <minlikelihood translate="label tooltip">
110
+ <label>Min Likelihood</label>
111
+ <frontend_type>text</frontend_type>
112
+ <backend_model>recommend/system_config_validation_minlikelihood</backend_model>
113
+ <sort_order>90</sort_order>
114
+ <show_in_default>1</show_in_default>
115
+ <show_in_website>1</show_in_website>
116
+ <show_in_store>1</show_in_store>
117
+ <tooltip>The minimum likelihood of purchase for a cross-sell or up-sell product to be included in the recommendation tables. In other words, products with a likelihood percentage below the minimum are not recommended, usually 5%</tooltip>
118
+ </minlikelihood>
119
+
120
+ <mincommon translate="label tooltip">
121
+ <label>Min Common</label>
122
+ <frontend_type>text</frontend_type>
123
+ <backend_model>recommend/system_config_validation_mincommon</backend_model>
124
+ <sort_order>100</sort_order>
125
+ <show_in_default>1</show_in_default>
126
+ <show_in_website>1</show_in_website>
127
+ <show_in_store>1</show_in_store>
128
+ <tooltip>The minimum number of common users for a cross-sell or up-sell product to be included in the recommendation tables. In other words, products with common users below the minimum are not included in the tables. Usually this is set to 2, such that product pairs bought by only 1 customer are not recommended.</tooltip>
129
+ </mincommon>
130
+
131
+ <mode translate="label tooltip">
132
+ <label>Mode</label>
133
+ <frontend_type>select</frontend_type>
134
+ <source_model>recommend/system_config_source_mode</source_model>
135
+ <sort_order>110</sort_order>
136
+ <show_in_default>1</show_in_default>
137
+ <show_in_website>1</show_in_website>
138
+ <show_in_store>1</show_in_store>
139
+ <tooltip>Select the mode Live to Test.</tooltip>
140
+ </mode>
141
+
142
+ <maxsalesdataageinmonths translate="label tooltip">
143
+ <label>Max Sales Data Age In Months</label>
144
+ <frontend_type>text</frontend_type>
145
+ <backend_model>recommend/system_config_validation_maxsalesdataageinmonths</backend_model>
146
+ <sort_order>140</sort_order>
147
+ <show_in_default>1</show_in_default>
148
+ <show_in_website>1</show_in_website>
149
+ <show_in_store>1</show_in_store>
150
+ <tooltip>The number of months of sales data to upload when uploading all sales data.</tooltip>
151
+ </maxsalesdataageinmonths>
152
+ </fields>
153
+ </config>
154
+
155
+ <display_recommendation translate="label">
156
+ <label>Display Recommendations</label>
157
+ <comment>&lt;?php echo "xyz"; ?&gt;</comment>
158
+ <frontend_type>text</frontend_type>
159
+ <sort_order>20</sort_order>
160
+ <show_in_default>1</show_in_default>
161
+ <show_in_website>1</show_in_website>
162
+ <show_in_store>1</show_in_store>
163
+ <fields>
164
+ <upsell translate="label tooltip">
165
+ <label>Upsell</label>
166
+ <frontend_type>select</frontend_type>
167
+ <source_model>recommend/system_config_source_upsell</source_model>
168
+ <sort_order>10</sort_order>
169
+ <show_in_default>1</show_in_default>
170
+ <show_in_website>1</show_in_website>
171
+ <show_in_store>1</show_in_store>
172
+ <tooltip>Retrieve recommended Upsell products from 4-Tell</tooltip>
173
+ </upsell>
174
+ <numupsell translate="label tooltip">
175
+ <label>Number of Recommendations</label>
176
+ <frontend_type>text</frontend_type>
177
+ <backend_model>recommend/system_config_validation_numupsell</backend_model>
178
+ <sort_order>15</sort_order>
179
+ <show_in_default>1</show_in_default>
180
+ <show_in_website>1</show_in_website>
181
+ <show_in_store>1</show_in_store>
182
+ <tooltip>How many recommended products to retrieve for Upsell</tooltip>
183
+ </numupsell>
184
+
185
+ <crosssell translate="label tooltip">
186
+ <label>Cross Sell</label>
187
+ <frontend_type>select</frontend_type>
188
+ <source_model>recommend/system_config_source_crosssell</source_model>
189
+ <sort_order>20</sort_order>
190
+ <show_in_default>1</show_in_default>
191
+ <show_in_website>1</show_in_website>
192
+ <show_in_store>1</show_in_store>
193
+ <tooltip>Retrieve recommended Upsell products from 4-Tell</tooltip>
194
+ </crosssell>
195
+ <numcrosssell translate="label tooltip">
196
+ <label>Number of Recommendations</label>
197
+ <frontend_type>text</frontend_type>
198
+ <backend_model>recommend/system_config_validation_numcrosssell</backend_model>
199
+ <sort_order>25</sort_order>
200
+ <show_in_default>1</show_in_default>
201
+ <show_in_website>1</show_in_website>
202
+ <show_in_store>1</show_in_store>
203
+ <tooltip>How many recommended products to retrieve for Cross Sell</tooltip>
204
+ </numcrosssell>
205
+
206
+ <related translate="label tooltip">
207
+ <label>Related</label>
208
+ <frontend_type>select</frontend_type>
209
+ <source_model>recommend/system_config_source_related</source_model>
210
+ <sort_order>30</sort_order>
211
+ <show_in_default>1</show_in_default>
212
+ <show_in_website>1</show_in_website>
213
+ <show_in_store>1</show_in_store>
214
+ <tooltip>Retrieve recommended Upsell products from 4-Tell</tooltip>
215
+ </related>
216
+ <numrelated translate="label tooltip">
217
+ <label>Number of Recommendations</label>
218
+ <frontend_type>text</frontend_type>
219
+ <backend_model>recommend/system_config_validation_numrelated</backend_model>
220
+ <sort_order>35</sort_order>
221
+ <show_in_default>1</show_in_default>
222
+ <show_in_website>1</show_in_website>
223
+ <show_in_store>1</show_in_store>
224
+ <tooltip>How many recommended products to retrieve for Related products</tooltip>
225
+ </numrelated>
226
+ </fields>
227
+ </display_recommendation>
228
+
229
+ <gogle_analytics translate="label">
230
+ <label>Google Analytics</label>
231
+ <frontend_type>text</frontend_type>
232
+ <sort_order>30</sort_order>
233
+ <show_in_default>1</show_in_default>
234
+ <show_in_website>1</show_in_website>
235
+ <show_in_store>1</show_in_store>
236
+ <fields>
237
+ <gaenabled translate="label tooltip">
238
+ <label>Google Analytics enabled</label>
239
+ <frontend_type>label</frontend_type>
240
+ <frontend_model>recommend/system_config_google_analytics_status</frontend_model>
241
+ <sort_order>5</sort_order>
242
+ <show_in_default>1</show_in_default>
243
+ <show_in_website>1</show_in_website>
244
+ <show_in_store>1</show_in_store>
245
+ <tooltip>Shows if Google Analytics really enabled in the system configuration</tooltip>
246
+ </gaenabled>
247
+ <ga_code translate="label tooltip">
248
+ <label>Google Account ID</label>
249
+ <frontend_type>label</frontend_type>
250
+ <frontend_model>recommend/system_config_google_analytics_code</frontend_model>
251
+ <sort_order>20</sort_order>
252
+ <show_in_default>1</show_in_default>
253
+ <show_in_website>1</show_in_website>
254
+ <show_in_store>1</show_in_store>
255
+ <tooltip>Your web property ID (UA-XXXXX-X)</tooltip>
256
+ </ga_code>
257
+ <ga_change translate="label tooltip">
258
+ <label>Google Credentials</label>
259
+ <frontend_type>label</frontend_type>
260
+ <frontend_model>recommend/system_config_google_analytics_change</frontend_model>
261
+ <sort_order>30</sort_order>
262
+ <show_in_default>1</show_in_default>
263
+ <show_in_website>1</show_in_website>
264
+ <show_in_store>1</show_in_store>
265
+ <tooltip>You can change your google analytics settings here</tooltip>
266
+ </ga_change>
267
+ </fields>
268
+ </gogle_analytics>
269
+ <cron_jobs translate="label">
270
+ <label>Cron Jobs</label>
271
+ <frontend_type>text</frontend_type>
272
+ <sort_order>40</sort_order>
273
+ <show_in_default>1</show_in_default>
274
+ <show_in_website>1</show_in_website>
275
+ <show_in_store>1</show_in_store>
276
+ <fields>
277
+ <sales translate="label tooltip">
278
+ <label>Sales data</label>
279
+ <frontend_type>select</frontend_type>
280
+ <source_model>
281
+ recommend/upload_frequency
282
+ </source_model>
283
+ <sort_order>10</sort_order>
284
+ <show_in_default>1</show_in_default>
285
+ <show_in_website>1</show_in_website>
286
+ <show_in_store>1</show_in_store>
287
+ <tooltip>How often we should start Sales data upload process</tooltip>
288
+ </sales>
289
+ <catalog translate="label tooltip">
290
+ <label>Catalog data</label>
291
+ <frontend_type>select</frontend_type>
292
+ <source_model>recommend/upload_frequency</source_model>
293
+ <sort_order>20</sort_order>
294
+ <show_in_default>1</show_in_default>
295
+ <show_in_website>1</show_in_website>
296
+ <show_in_store>1</show_in_store>
297
+ <tooltip>How often we should start Catalog data upload process</tooltip>
298
+ </catalog>
299
+ </fields>
300
+ </cron_jobs>
301
+ </groups>
302
+ </recommend>
303
+ </sections>
304
+ </config>
app/code/community/FourTell/Recommend/etc/upload.xml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <upload_options>
3
+ <exclude_options/>
4
+ <replace_options/>
5
+ <upload_sales_data>All</upload_sales_data>
6
+ <upload_replacement_data>None</upload_replacement_data>
7
+ <upload_product_data>Yes</upload_product_data>
8
+ <upload_exclusion_data>No</upload_exclusion_data>
9
+ <replace_catalog_old_field>sku</replace_catalog_old_field>
10
+ <replace_catalog_new_field>entity_id</replace_catalog_new_field>
11
+ <showUploadDetails>Yes</showUploadDetails>
12
+ </upload_options>
app/code/community/FourTell/Recommend/etc/widget.xml ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <widgets>
3
+ <fourtell_widget type="recommend/widget">
4
+ <name>4-Tell: Recommendation Widget</name>
5
+ <description type="desc">Adds Recommendation Products</description>
6
+ <is_email_compatible>1</is_email_compatible>
7
+
8
+ <parameters>
9
+ <recommendation_type>
10
+ <required>1</required>
11
+ <visible>1</visible>
12
+ <label>Type of Recommendtions</label>
13
+ <description>Type of Recommendtion products to retrieve from 4-Tell Recommendation Service</description>
14
+ <type>select</type>
15
+
16
+ <values>
17
+ <one translate="label">
18
+ <value>topsellers</value>
19
+ <label>Top Sellers</label>
20
+ </one>
21
+ <two translate="label">
22
+ <value>Personalized</value>
23
+ <label>Personalized</label>
24
+ </two>
25
+ <three translate="label">
26
+ <value>crosssell</value>
27
+ <label>Cross Sell</label>
28
+ </three>
29
+ </values>
30
+ </recommendation_type>
31
+
32
+ <num_recommendations>
33
+ <required>1</required>
34
+ <visible>1</visible>
35
+ <label>Number of Products</label>
36
+ <description>Number of products to retrieve from 4-Tell Recommendation Service</description>
37
+ <type>text</type>
38
+ </num_recommendations>
39
+
40
+ <in_line>
41
+ <required>1</required>
42
+ <visible>1</visible>
43
+ <label>Number of Products per Line</label>
44
+ <description>Number of products per one line</description>
45
+ <type>text</type>
46
+ </in_line>
47
+
48
+ <slider_enable>
49
+ <required>1</required>
50
+ <visible>1</visible>
51
+ <label>Enable Slider</label>
52
+ <description>Enable or disable slider for product blocks</description>
53
+ <type>select</type>
54
+ <value>1</value>
55
+ <values>
56
+ <default translate="label">
57
+ <value>1</value>
58
+ <label>Yes</label>
59
+ </default>
60
+ <list translate="label">
61
+ <value>0</value>
62
+ <label>No</label>
63
+ </list>
64
+ </values>
65
+ </slider_enable>
66
+
67
+ <heading>
68
+ <required>0</required>
69
+ <visible>1</visible>
70
+ <label>Heading for Products</label>
71
+ <description>Heading to display above products</description>
72
+ <type>text</type>
73
+ </heading>
74
+
75
+ <cssclass>
76
+ <required>0</required>
77
+ <visible>1</visible>
78
+ <label>Widget CSS Class</label>
79
+ <description>Override default css class with your own</description>
80
+ <type>text</type>
81
+ </cssclass>
82
+
83
+ <gafunction>
84
+ <required>0</required>
85
+ <visible>1</visible>
86
+ <label>Google Analytics Function</label>
87
+ <description>The JavaScript function for Google Analytics tracking</description>
88
+ <type>text</type>
89
+ </gafunction>
90
+
91
+ <template>
92
+ <required>1</required>
93
+ <visible>1</visible>
94
+ <label>Template</label>
95
+ <type>select</type>
96
+ <value>catalog/product/widget/new/content/new_grid.phtml</value>
97
+ <values>
98
+ <default translate="label">
99
+ <value>catalog/product/widget/new/content/new_grid.phtml</value>
100
+ <label>New Products Grid Template</label>
101
+ </default>
102
+ <list translate="label">
103
+ <value>catalog/product/widget/new/content/new_list.phtml</value>
104
+ <label>New Products List Template</label>
105
+ </list>
106
+ </values>
107
+ </template>
108
+
109
+ </parameters>
110
+ </fourtell_widget>
111
+ </widgets>
app/design/adminhtml/default/default/layout/recommend.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+ <recommend_adminhtml_uploadform_index>
4
+ <update handle="recommend_uploadform_index"/>
5
+
6
+ <reference name="content">
7
+ <block type="adminhtml/template" name="uploadform" template="recommend/uploadform.phtml"/>
8
+ </reference>
9
+ </recommend_adminhtml_uploadform_index>
10
+ </layout>
app/design/adminhtml/default/default/template/recommend/uploadform.phtml ADDED
@@ -0,0 +1,625 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <style>
2
+ #loading-mask .loader {
3
+ position: inherit !important;
4
+ top:20%;
5
+ }
6
+ </style>
7
+ <?php
8
+ $ex = Mage::getSingleton('FourTell_Recommend_Model_UploadSetting')->load();
9
+ $attrs = Mage::getSingleton('FourTell_Recommend_Adminhtml_UploadformController');
10
+ $attrs->__construct(Mage::app()->getRequest());
11
+ $attrs->getProductAttributes();
12
+ $cfgLink = Mage::helper('recommend')->getCfgLink();
13
+ $comps = Mage::helper('recommend')->getCompsArray();
14
+ $options = Mage::helper('recommend')->getOptionsArray();
15
+ $rowIdx = 1;
16
+ ?>
17
+
18
+ <form id="edit_form" name="edit_form" method="post" action="">
19
+ <input name="form_key" type="hidden" value="<?php echo Mage::getSingleton('core/session')->getFormKey() ?>" />
20
+ <input type="hidden" id="what_data" name="what_data" value="" />
21
+
22
+ <div class="content-headerx" style="padding-bottom: 20px;">
23
+ <table cellspacing="0" class="grid-header">
24
+ <tr>
25
+ <td><h3>4-Tell Recommend</h3></td>
26
+ </tr>
27
+ <tr>
28
+ <td>
29
+ Select the data to be uploaded and the press the <b>Upload Data</b> button.
30
+ If this is your first upload, select "All" sales data.
31
+ </td>
32
+ </tr>
33
+ <tr>
34
+ <td>
35
+ <a href="<?php echo $cfgLink; ?>"><?php echo $this->__('Go to 4-Tell System Configuration'); ?></a>
36
+ </td>
37
+ </tr>
38
+ </table>
39
+ </div>
40
+
41
+
42
+ <div class="content-header">
43
+ <table cellspacing="0" class="grid-header">
44
+ <tr>
45
+ <td><h3>Upload Options</h3></td>
46
+ <td class="a-right">
47
+ <button id="saveButton" onclick="$('what_data').setAttribute('value', 'save_settings'); editForm.submit();" class="scalable" type="button" value="save settings" style="display: none;"><span>Save Settings</span></button>
48
+ <?php if (Mage::helper('recommend')->isEnabled()) { ?>
49
+ <button id="uploadButton" onclick="$('what_data').setAttribute('value', 'upload_data'); editForm.submit();" class="scalable" type="button" value="upload_data" style="display: none;"><span>Upload Data</span></button>
50
+ <?php } ?>
51
+ <button id="ajaxButton" onclick="javascript:ajaxUpload(0);" class="scalable" type="button" value="Upload Data" style="display: none;"><span>Upload Data</span></button>
52
+ </td>
53
+ </tr>
54
+ </table>
55
+ </div>
56
+ <div class="content-header" style="border: 0px !important;">
57
+ <table cellspacing="0" class="grid-header" id="sales_options">
58
+ <tbody>
59
+ <tr>
60
+ <td style="width: 200px; white-space: nowrap;"><h3>Upload Sales Data</h3></td>
61
+ <td>
62
+ <select id="upload_sales_data" name="upload_sales_data" style="width: 120px;">
63
+ <option value="All" <?php if (trim($ex['upload_sales_data']) == "All") { echo 'selected="selected"'; } ?>>All</option>
64
+ <option value="New" <?php if (trim($ex['upload_sales_data']) == "New") { echo 'selected="selected"'; } ?>>New</option>
65
+ <option value="None" <?php if (trim($ex['upload_sales_data']) == "None") { echo 'selected="selected"'; } ?>>None</option>
66
+ </select>
67
+ </td>
68
+ <td>&nbsp;</td>
69
+ </tr>
70
+
71
+ <tr>
72
+ <td style="width: 200px; white-space: nowrap;"><h3>Upload Catalog Data</h3></td>
73
+ <td>
74
+ <select id="upload_product_data" name="upload_product_data" style="width: 120px;">
75
+ <option value="Yes" <?php if (trim($ex['upload_product_data']) == "Yes") { echo 'selected="selected"'; } ?>>Yes</option>
76
+ <option value="No" <?php if (trim($ex['upload_product_data']) == "No") { echo 'selected="selected"'; } ?>>No</option>
77
+ </select>
78
+ </td>
79
+ <td>&nbsp;</td>
80
+ </tr>
81
+
82
+ <tr>
83
+ <td style="width: 200px; white-space: nowrap;"><h3>Enable Exclusion Data</h3></td>
84
+ <td>
85
+ <select name="upload_exclusion_data" id="upload_exclusion_data" style="width: 120px;">
86
+ <option value="No" <?php if (trim($ex['upload_exclusion_data']) == "No") { echo 'selected="selected"'; } ?>>No</option>
87
+ <option value="Yes" <?php if (trim($ex['upload_exclusion_data']) == "Yes") { echo 'selected="selected"'; } ?>>Yes</option>
88
+ </select>
89
+
90
+ <div class="content-header" id="exclude_table_container" <?php if (trim($ex['upload_exclusion_data']) == "No") { ?> style="display: none;" <?php } ?>>
91
+ <table cellspacing="0" class="grid-header" id="exclude_table">
92
+ <tbody>
93
+ <tr>
94
+ <td><h3>Field</h3></td>
95
+ <td><h3>Comparison</h3></td>
96
+ <td><h3>Value</h3></td>
97
+ <td>&nbsp;</td>
98
+ </tr>
99
+
100
+ <?php
101
+ $rowIdx = 1;
102
+ foreach ($ex['exclude_options'] as $o) {
103
+ ?>
104
+ <tr class="excludeRow" id="excludeRow_<?php echo $rowIdx; ?>">
105
+ <td>
106
+ <select id="exclude_field" name="excludeField_Name_<?php echo $rowIdx; ?>">
107
+ <option value=""></option>
108
+ <?php foreach ($attrs as $v): ?>
109
+ <option value="<?php echo htmlspecialchars($v['name']); ?>" <?php if($v['name']==$o['field']): ?>selected="selected"<?php endif ?>><?php echo htmlspecialchars(str_replace('\'', '', $v['label'])); ?></option>
110
+ <?php endforeach ?>
111
+ </select>
112
+ </td>
113
+ <td>
114
+ <select id="exclude_field_compare" name="excludeField_Compare_<?php echo $rowIdx; ?>" style="width: 180px;">
115
+ <?php
116
+ foreach ($comps as $k => $v)
117
+ {
118
+ $selected = "";
119
+ if ($k == $o['compare'])
120
+ {
121
+ $selected = "selected";
122
+ }
123
+ ?>
124
+ <option value="<?php echo $k; ?>" <?php if ($selected != "") { ?>selected="selected"<?php } ?>><?php echo $v; ?></option>
125
+ <?php
126
+ }
127
+ ?>
128
+ </select>
129
+ </td>
130
+ <td><input id="exclude_field_value" name="excludeField_Value_<?php echo $rowIdx; ?>" type="text" size="40" value="<?php echo $o['value']; ?>" /></td>
131
+ <td class="a-right">
132
+ <button onclick="javascript:removeExcludeRow(<?php echo $rowIdx; ?>);" class="scalable" type="button" value=" x "><span> x </span></button>
133
+ </td>
134
+ </tr>
135
+ <?php
136
+ $rowIdx++;
137
+ }
138
+ ?>
139
+ </tbody>
140
+ </table>
141
+ </div>
142
+ </td>
143
+ </tr>
144
+
145
+ <tr>
146
+ <td style="width: 200px; white-space: nowrap;"><h3>Enable Replacement Data</h3></td>
147
+ <td>
148
+ <select name="upload_replacement_data" id="upload_replacement_data" style="width: 120px;">
149
+ <option value="None" <?php if (trim($ex['upload_replacement_data']) == "None") { echo 'selected="selected"'; } ?>>None</option>
150
+ <option value="Entire Catalog" <?php if (trim($ex['upload_replacement_data']) == "Entire Catalog") { echo 'selected="selected"'; } ?>>Entire Catalog</option>
151
+ <option value="Individual Items" <?php if (trim($ex['upload_replacement_data']) == "Individual Items") { echo 'selected="selected"'; } ?>>Individual Items</option>
152
+ </select>
153
+
154
+ <div class="content-header" id="replacement_catalog_table_container" <?php if (trim($ex['upload_replacement_data']) != "Entire Catalog") { ?> style="display: none;" <?php } ?>>
155
+ <table cellspacing="0" class="grid-header" id="replacement_table">
156
+ <tr class="excludeRow" id="replacement_header">
157
+ <td><h3>Old Field</h3></td>
158
+ <td><h3>New Field</h3></td>
159
+ <td>&nbsp;</td>
160
+ </tr>
161
+ <tr class="excludeRow" id="replacement_data">
162
+ <td style="padding-right: 20px;">
163
+ <select name="replace_catalog_old_field">
164
+ <option value=""></option>
165
+ <?php foreach ($attrs as $v): ?>
166
+ <?php if(strlen(trim($v['label']))==0) continue; ?>
167
+ <option value="<?php echo htmlspecialchars($v['name']); ?>" <?php if($v['name']==$ex['replace_catalog_old_field']): ?>selected="selected"<?php endif ?>><?php echo htmlspecialchars(str_replace('\'', '', $v['label'])); ?></option>
168
+ <?php endforeach ?>
169
+ </select>
170
+ </td>
171
+ <td style="padding-right: 20px;">
172
+ <select name="replace_catalog_new_field">
173
+ <option value=""></option>
174
+ <?php foreach ($attrs as $v): ?>
175
+ <?php if(strlen(trim($v['label']))==0) continue; ?>
176
+ <option value="<?php echo htmlspecialchars($v['name']); ?>" <?php if($v['name']==$ex['replace_catalog_new_field']): ?>selected="selected"<?php endif ?>><?php echo htmlspecialchars(str_replace('\'', '', $v['label'])); ?></option>
177
+ <?php endforeach ?>
178
+ </select>
179
+ </td>
180
+ <td style="width: 100%;">&nbsp;</td>
181
+ </tr>
182
+ </table>
183
+ </div>
184
+
185
+ <div class="content-header" id="replacement_item_table_container" <?php if (trim($ex['upload_replacement_data']) != "Individual Items") { ?> style="display: none;" <?php } ?>>
186
+ <table cellspacing="0" class="grid-header" id="replace_table">
187
+ <tbody>
188
+ <tr>
189
+ <td><h3>Old ID</h3></td>
190
+ <td><h3>New ID</h3></td>
191
+ <td>&nbsp;</td>
192
+ </tr>
193
+
194
+ <?php
195
+ $repIdx = 1;
196
+ foreach ($ex['replace_options'] as $o) {
197
+ ?>
198
+ <tr class="replaceRow" id="replaceRow_<?php echo $repIdx; ?>">
199
+ <td>
200
+ <select id="replace_field_oldid" name="replaceField_oldid_<?php echo $repIdx; ?>">
201
+ <option value=""></option>
202
+ <?php foreach ($attrs as $v): ?>
203
+ <?php if(strlen(trim($v['label']))==0) continue; ?>
204
+ <option value="<?php echo htmlspecialchars($v['name']); ?>" <?php if($v['name']==$o['oldid']): ?>selected="selected"<?php endif ?>><?php echo htmlspecialchars(str_replace('\'', '', $v['label'])); ?></option>
205
+ <?php endforeach ?>
206
+ </select>
207
+ </td>
208
+ <td>
209
+ <select id="replace_field_newid" name="replaceField_newid_<?php echo $repIdx; ?>">
210
+ <option value=""></option>
211
+ <?php foreach ($attrs as $v): ?>
212
+ <?php if(strlen(trim($v['label']))==0) continue; ?>
213
+ <option value="<?php echo htmlspecialchars($v['name']); ?>" <?php if($v['name']==$o['newid']): ?>selected="selected"<?php endif ?>><?php echo htmlspecialchars(str_replace('\'', '', $v['label'])); ?></option>
214
+ <?php endforeach ?>
215
+ </select>
216
+ </td>
217
+ <td class="a-right">
218
+ <button onclick="javascript:removeReplaceRow(<?php echo $repIdx; ?>);" class="scalable" type="button" value=" x "><span> x </span></button>
219
+ </td>
220
+ </tr>
221
+ <?php
222
+ $repIdx++;
223
+ }
224
+ ?>
225
+ </tbody>
226
+ </table>
227
+ </div>
228
+ </td>
229
+ </tr>
230
+ <tr>
231
+ <td style="width: 200px; white-space: nowrap;"><h3>Show Upload Details</h3></td>
232
+ <td>
233
+ <select name="showUploadDetails" id="showUploadDetails" style="width: 120px;">
234
+ <option value="No" <?php if (trim($ex['showUploadDetails']) == "No") { echo 'selected="selected"'; } ?>>No</option>
235
+ <option value="Yes" <?php if (trim($ex['showUploadDetails']) == "Yes") { echo 'selected="selected"'; } ?>>Yes</option>
236
+ </select>
237
+ </td>
238
+ </tr>
239
+ </tbody>
240
+ </table>
241
+ </div>
242
+ </form>
243
+
244
+
245
+ <div id="loading-mask" style="display:none">
246
+ <p class="loader" id="loading_mask_loader">
247
+ <img src="<?php echo Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB); ?>/skin/adminhtml/default/default/images/ajax-loader-tr.gif" alt="Loading..."/>
248
+ <span id="loading-msg">Uploading data, please wait...</span>
249
+ </p>
250
+ </div>
251
+
252
+
253
+ <script type="text/javascript">
254
+ var editForm = new varienForm('edit_form');
255
+ var numExs = <?php echo count($ex['exclude_options']); ?>;
256
+
257
+ function removeExcludeRow(row) {
258
+ var rowEl = $$('#excludeRow_'+row);
259
+ rowEl.each(function(el) {
260
+ el.remove();
261
+ });
262
+
263
+ setSaveButton('block');
264
+ }
265
+
266
+ function makeExcludeSelect(id) {
267
+ var sel = new Element('select', {'style': 'width: 180px'});
268
+ sel.writeAttribute('name', 'excludeField_Compare_'+id);
269
+
270
+ <?php
271
+ foreach ($comps as $k => $v) {
272
+ ?>
273
+ sel.insert(new Element('option', {'value': '<?php echo $k; ?>', text: '<?php echo $v; ?>'}).update('<?php echo $v; ?>'));
274
+ <?php } ?>
275
+
276
+ return sel;
277
+ }
278
+
279
+ function makeExcludeButton(id) {
280
+ var js = 'javascript:addExcludeRow(true); setSaveButton("block");';
281
+ var but = new Element('button', {'id': 'excludeFieldButton_'+id, 'onclick': js, 'class': 'scalable', 'type': 'button', 'value': ' + '});
282
+
283
+ but.insert(new Element('span').update(' + '));
284
+
285
+ return but;
286
+ }
287
+
288
+ function swapExcludeButton(id) {
289
+ var buts = $$('#excludeFieldButton_'+id);
290
+ buts.each(function(but) {
291
+ but.down().update(' x ');
292
+ but.setAttribute('onclick', 'javascript:removeExcludeRow('+id+');');
293
+ });
294
+ }
295
+
296
+ function addExcludeRow(doSwap) {
297
+ numExs++;
298
+
299
+ $$('#exclude_table tbody').each(function(tbl) {
300
+ var tr = new Element('tr', {'id': 'excludeRow_' + numExs, 'class': 'excludeRow'});
301
+
302
+ var newEl = new Element('select');
303
+ newEl.writeAttribute('id', 'excludeField_Name_'+numExs);
304
+ newEl.writeAttribute('name', 'excludeField_Name_'+numExs);
305
+ var option = new Element("option");
306
+ option.text = '';
307
+ option.value = '';
308
+ newEl.insert(option);
309
+ <?php
310
+ foreach ($attrs as $k => $v){
311
+ if(strlen(trim($v['label']))=='') continue;
312
+ ?>
313
+ var option = new Element("option");
314
+ option.text = '<?php echo htmlspecialchars(str_replace('\'', '', $v['label'])); ?>';
315
+ option.value = '<?php echo htmlspecialchars($v['name']); ?>';
316
+ newEl.insert(option);
317
+ <?php
318
+ }
319
+ ?>
320
+ tr.insert(new Element('td').update(newEl));
321
+ tr.insert(new Element('td').update(makeExcludeSelect(numExs)));
322
+ var newEl = new Element('input', {'type': 'text', 'size': '40' });
323
+ newEl.writeAttribute('id', 'excludeField_Value_'+numExs);
324
+ newEl.writeAttribute('name', 'excludeField_Value_'+numExs);
325
+ tr.insert(new Element('td').update(newEl));
326
+
327
+ tr.insert(new Element('td', {'class': 'a-right'}).update(makeExcludeButton(numExs)));
328
+
329
+ tbl.insert(tr);
330
+
331
+ if (doSwap==true) {
332
+ swapExcludeButton(numExs-1);
333
+ }
334
+ });
335
+ }
336
+
337
+ var numReps = <?php echo count($ex['replace_options']); ?>;
338
+
339
+ function removeReplaceRow(row) {
340
+ var rowEl = $$('#replaceRow_'+row);
341
+ rowEl.each(function(el) {
342
+ el.remove();
343
+ });
344
+
345
+ setSaveButton('block');
346
+ }
347
+
348
+ function makeReplaceButton(id) {
349
+ var js = 'javascript:addReplaceRow(true); setSaveButton("block");';
350
+ var but = new Element('button', {'id': 'replaceFieldButton_'+id, 'onclick': js, 'class': 'scalable', 'type': 'button', 'value': ' + '});
351
+
352
+ but.insert(new Element('span').update(' + '));
353
+
354
+ return but;
355
+ }
356
+
357
+ function swapReplaceButton(id) {
358
+ var buts = $$('#replaceFieldButton_'+id);
359
+ buts.each(function(but) {
360
+ but.down().update(' x ');
361
+ but.setAttribute('onclick', 'javascript:removeReplaceRow('+id+');');
362
+ });
363
+ }
364
+
365
+ function addReplaceRow(doSwap) {
366
+ numReps++;
367
+
368
+ $$('#replace_table tbody').each(function(tbl) {
369
+ var tr = new Element('tr', {'id': 'replaceRow_' + numReps, 'class': 'replaceRow'});
370
+
371
+ var newEl = new Element('select');
372
+ newEl.writeAttribute('id', 'replaceField_oldid_'+numReps);
373
+ newEl.writeAttribute('name', 'replaceField_oldid_'+numReps);
374
+
375
+ var option = new Element("option");
376
+ option.text = '';
377
+ option.value = '';
378
+ newEl.insert(option);
379
+ <?php
380
+ foreach ($attrs as $k => $v){
381
+ if(strlen(trim($v['label']))=='') continue;
382
+ ?>
383
+ var option = new Element("option");
384
+ option.text = '<?php echo htmlspecialchars(str_replace('\'', '', $v['label'])); ?>';
385
+ option.value = '<?php echo htmlspecialchars($v['name']); ?>';
386
+ newEl.insert(option);
387
+ <?php
388
+ }
389
+ ?>
390
+ tr.insert(new Element('td').update(newEl));
391
+
392
+ var newEl = new Element('select');
393
+ var option = new Element("option");
394
+ option.text = '';
395
+ option.value = '';
396
+ newEl.insert(option);
397
+ <?php
398
+ foreach ($attrs as $k => $v){
399
+ if(strlen(trim($v['label']))=='') continue;
400
+ ?>
401
+ var option = new Element("option");
402
+ option.text = '<?php echo htmlspecialchars(str_replace('\'', '', $v['label'])); ?>';
403
+ option.value = '<?php echo htmlspecialchars($v['name']); ?>';
404
+ newEl.insert(option);
405
+ <?php
406
+ }
407
+ ?>
408
+ newEl.writeAttribute('id', 'replaceField_newid_'+numReps);
409
+ newEl.writeAttribute('name', 'replaceField_newid_'+numReps);
410
+ tr.insert(new Element('td').update(newEl));
411
+
412
+ tr.insert(new Element('td', {'class': 'a-right', 'style': 'text-align: right !important;'}).update(makeReplaceButton(numReps)));
413
+
414
+ tbl.insert(tr);
415
+
416
+ if (doSwap==true) {
417
+ swapReplaceButton(numReps-1);
418
+ }
419
+ });
420
+ }
421
+
422
+
423
+
424
+
425
+
426
+ document.observe("dom:loaded", function() {
427
+ addExcludeRow(false);
428
+ addReplaceRow(false);
429
+
430
+ var ub = $('uploadButton');
431
+ var ab = $('ajaxButton');
432
+
433
+ ub.setStyle({display: 'none'});
434
+ ab.setStyle({display: 'block'});
435
+ });
436
+
437
+ Event.observe($('upload_exclusion_data'), 'change', function(event) {
438
+ var sel = $('upload_exclusion_data').getValue();
439
+ if (sel == "No") {
440
+ $('exclude_table_container').setStyle({'display': 'none'});
441
+ } else {
442
+ $('exclude_table_container').setStyle({'display': 'block'});
443
+ }
444
+
445
+ setSaveButton('block');
446
+ });
447
+
448
+ Event.observe($('upload_replacement_data'), 'change', function(event) {
449
+ var sel = $('upload_replacement_data').getValue();
450
+ if (sel == "None") {
451
+ $('replacement_catalog_table_container').setStyle({'display': 'none'});
452
+ $('replacement_item_table_container').setStyle({'display': 'none'});
453
+
454
+ } else if (sel == "Entire Catalog") {
455
+ $('replacement_catalog_table_container').setStyle({'display': 'block'});
456
+ $('replacement_item_table_container').setStyle({'display': 'none'});
457
+
458
+ } else if (sel == "Individual Items") {
459
+ $('replacement_catalog_table_container').setStyle({'display': 'none'});
460
+ $('replacement_item_table_container').setStyle({'display': 'block'});
461
+
462
+ }
463
+
464
+ setSaveButton('block');
465
+ });
466
+
467
+ Event.observe($('upload_sales_data'), 'change', function(event) {
468
+ setSaveButton('block');
469
+ });
470
+
471
+ Event.observe($('upload_product_data'), 'change', function(event) {
472
+ setSaveButton('block');
473
+ });
474
+
475
+ Event.observe($('showUploadDetails'), 'change', function(event) {
476
+ setSaveButton('block');
477
+ });
478
+
479
+ $$('select').each(function(el) {
480
+ Event.observe(el, 'change', function(event) {
481
+ setSaveButton('block');
482
+ });
483
+ });
484
+
485
+ $$('input').each(function(el) {
486
+ Event.observe(el, 'change', function(event) {
487
+ setSaveButton('block');
488
+ });
489
+ });
490
+
491
+
492
+ function setSaveButton(status) {
493
+ $('saveButton').setStyle({'display': status});
494
+ if (status == 'block') {
495
+ setUploadButton('none');
496
+ } else {
497
+ setUploadButton('block');
498
+ }
499
+ }
500
+
501
+ function setUploadButton(status) {
502
+ $('uploadButton').setStyle({'display': status});
503
+ $('ajaxButton').setStyle({'display': status});
504
+ }
505
+
506
+ function getUrlKeyValue(str, key) {
507
+ var sub = "(/"+key+"/)(.*?/)";
508
+ var re = new RegExp(sub, "g");
509
+ var ret = str.match(re);
510
+
511
+ if (ret && ret.length) {
512
+ var vals = ret[0].split('/');
513
+ if (vals && vals.length > 1) {
514
+ var out = vals[2];
515
+ return out;
516
+ }
517
+ }
518
+
519
+ return null;
520
+ }
521
+
522
+ function ajaxUpload(pos, gourl) {
523
+ Element.show('loading-mask');
524
+
525
+ var dt = '<?php echo date("Y-m"); ?>';
526
+ var files = new Array(
527
+ {upload: 1, key: 'configboost', name: 'Uploading<br />Configuration', command: 'ucb', field: null, value: null},
528
+ {upload: 1, key: 'categories', name: 'Uploading<br />Categories', command: 'ucd', field: null, value: null},
529
+ {upload: <?php echo strtolower($ex['upload_exclusion_data']) != "no" ? 1 : 0 ?>, key: 'exclusions', name: 'Uploading<br />Exclusions', command: 'ued', field: null, value: null},
530
+ {upload: <?php echo strtolower($ex['upload_replacement_data']) != "none" ? 1 : 0 ?>, key: 'replacements', name: 'Uploading<br />Replacements', command: 'urd', field: null, value: null},
531
+ {upload: <?php echo strtolower($ex['upload_product_data']) != "no" ? 1 : 0 ?>, key: 'products', name: 'Uploading<br />Products', command: 'upd', field: null, value: null},
532
+ {upload: <?php echo strtolower($ex['upload_sales_data']) != "none" ? 1 : 0 ?>, key: 'sales', name: 'Uploading<br />Sales', command: 'usf/ym/' + dt + '/p/0/isajax/true', field: 'status', value: 'completed'},
533
+ {upload: 1, key: 'generate', name: 'Starting<br />Generate', command: 'gen', field: null, value: null}
534
+ );
535
+
536
+ var doUpload = false;
537
+
538
+ for (var uf=pos; uf<files.length; uf++) {
539
+ if (files[uf].upload==1) {
540
+ doUpload = true;
541
+ pos = uf;
542
+ break;
543
+ }
544
+ }
545
+
546
+ if (doUpload) {
547
+ //handle situation when we in the URL do not have '/key/' part
548
+ var baseUrl = document.location.href.substr(0, document.location.href.indexOf('/key/'));
549
+ if( baseUrl.length == 0 ) {
550
+ baseUrl = document.location.href;
551
+ }
552
+ var reloadurl = baseUrl +
553
+ '/key/' + getUrlKeyValue(document.location.href, 'key') +
554
+ '/cmd/' + files[pos].command;
555
+
556
+ reloadurl = reloadurl.replace(document.location.origin, '');
557
+
558
+ Element.show('loading-mask');
559
+
560
+ if (gourl != undefined) {
561
+ reloadurl = gourl;
562
+ }
563
+
564
+ var name = files[pos].name;
565
+ if (pos == 5) {
566
+ var m = getUrlKeyValue(reloadurl, 'ym');
567
+ if (m) {
568
+ name = name + ' for ' + m;
569
+ }
570
+
571
+ var page = getUrlKeyValue(reloadurl, 'p');
572
+ if (page) {
573
+ name = name + ' Page: ' + (parseInt(page)+1);
574
+ }
575
+ }
576
+
577
+ Element.update('loading-msg', '<br />' + name + '<br />Please Wait...');
578
+
579
+ new Ajax.Request(reloadurl, {
580
+ method: 'get',
581
+ //asynchronous: false,
582
+ onComplete: function(transport) {
583
+ Element.hide('loading-mask');
584
+
585
+ $('loading-mask').setStyle({display: 'none'});
586
+
587
+ var tmp = transport.responseText.replace(new RegExp("[\r\n ]", "gmi"), "");
588
+ var res = tmp.replace(new RegExp("<!DOCTYPE.*", "gmi"), "");
589
+
590
+ if (res != "") {
591
+ var comp=res.evalJSON();
592
+
593
+ if (comp.status != "completed" && comp.month != "" && comp.month != "") {
594
+ //handle situation when we in the URL do not have '/key/' part
595
+ var baseUrl = document.location.href.substr(0, document.location.href.indexOf('/key/'));
596
+ if( baseUrl.length == 0 ) {
597
+ baseUrl = document.location.href;
598
+ }
599
+ reloadurl = baseUrl +
600
+ '/key/' + getUrlKeyValue(document.location.href, 'key') +
601
+ '/cmd/usf' +
602
+ '/ym/' + comp.month +
603
+ '/p/' + comp.page +
604
+ '/isajax/true' ;
605
+
606
+ ajaxUpload(pos, reloadurl);
607
+ } else {
608
+ pos++;
609
+ if (pos < files.length) {
610
+ ajaxUpload(pos);
611
+ }
612
+ }
613
+ } else {
614
+ pos++;
615
+ if (pos < files.length) {
616
+ ajaxUpload(pos);
617
+ }
618
+ }
619
+
620
+ }
621
+ });
622
+
623
+ }
624
+ }
625
+ </script>
app/design/frontend/base/default/layout/recommend.xml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <default>
4
+ <reference name="head">
5
+ <action method="addJs"><file>jquery.tools.min.js</file></action>
6
+ </reference>
7
+ </default>
8
+
9
+ <catalog_product_view>
10
+ <reference name="product.info">
11
+ <!-- remove name="catalog.product.related" / -->
12
+ <block type="recommend/catalog_product_list_related" name="catalog.product.related" as="relatedProducts" template="recommend/catalog/product/list/related.phtml">
13
+ <action method="setColumnCount"><columns>3</columns></action>
14
+ </block>
15
+ <!-- remove name="product.info.upsell" / -->
16
+ <block type="recommend/catalog_product_list_upsell" name="product.info.upsell" as="upsell_products" template="recommend/catalog/product/list/upsell.phtml">
17
+ <action method="setColumnCount"><columns>4</columns></action>
18
+ </block>
19
+ </reference>
20
+ </catalog_product_view>
21
+
22
+ <checkout_cart_index>
23
+ <reference name="checkout.cart">
24
+ <!-- remove name="checkout.cart.crosssell" / -->
25
+ <block type="recommend/checkout_cart_crosssell" name="checkout.cart.crosssell" as="crosssell" template="recommend/checkout/cart/crosssell.phtml" />
26
+ </reference>
27
+ </checkout_cart_index>
28
+ </layout>
app/design/frontend/base/default/template/recommend/catalog/product/list/related.phtml ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Academic Free License (AFL 3.0)
8
+ * that is bundled with this package in the file LICENSE_AFL.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/afl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category design
22
+ * @package base_default
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
25
+ */
26
+ //var_dump($this->getItems()->getSize());
27
+ //exit;
28
+ ?>
29
+ <?php
30
+ Mage::log('related_count: '.$this->getItems()->count());
31
+
32
+ if($this->getItems()->count()):
33
+ $gaUA = Mage::helper('recommend')->getGoogleAccountId();
34
+ $toutType = $this->getRecommendationType();
35
+ $gafunction='';
36
+ if( strlen($gaUA) > 0 && Mage::helper('recommend')->getGoogleAccountEnabled()) {
37
+ ?>
38
+ <script type="text/javascript">
39
+ //<![CDATA[
40
+ (function() {
41
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
42
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
43
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
44
+ })();
45
+ var _gaq = _gaq || [];
46
+ _gaq.push(['_setAccount', '<?php echo $gaUA; ?>']);
47
+ //]]>
48
+ </script>
49
+ <?php
50
+ }
51
+ ?>
52
+ <div class="block block-related">
53
+ <div class="block-title">
54
+ <strong><span><?php echo $this->__('Related Products') ?></span></strong>
55
+ </div>
56
+ <div class="block-content">
57
+ <p class="block-subtitle"><?php echo $this->__('Check items to add to the cart or') ?>&nbsp;<a href="#" onclick="selectAllRelated(this); return false;"><?php echo $this->__('select all') ?></a></p>
58
+ <ol class="mini-products-list" id="block-related">
59
+ <?php foreach($this->getItems() as $_item): ?>
60
+ <?php
61
+ if( strlen($gaUA) > 0 && Mage::helper('recommend')->getGoogleAccountEnabled()) {
62
+ $gafunction='onclick="_gaq.push([\'_trackEvent\', \'4TellRecs\', \''.$toutType.'\', \''.$_item->getEntityId().'\']);"';
63
+ }
64
+ ?>
65
+ <li class="item">
66
+ <?php if(!$_item->isComposite() && $_item->isSaleable()): ?>
67
+ <?php if (!$_item->getRequiredOptions()): ?>
68
+ <input type="checkbox" class="checkbox related-checkbox" id="related-checkbox<?php echo $_item->getId() ?>" name="related_products[]" value="<?php echo $_item->getId() ?>" />
69
+ <?php endif; ?>
70
+ <?php endif; ?>
71
+ <div class="product">
72
+ <a href="<?php echo $_item->getProductUrl() ?>" <?php echo $gafunction; ?> title="<?php echo $this->htmlEscape($_item->getName()) ?>" class="product-image"><img src="<?php echo $this->helper('catalog/image')->init($_item, 'thumbnail')->resize(50) ?>" width="50" height="50" alt="<?php echo $this->htmlEscape($_item->getName()) ?>" /></a>
73
+ <div class="product-details">
74
+ <p class="product-name"><a href="<?php echo $_item->getProductUrl() ?>" <?php echo $gafunction; ?>><?php echo $this->htmlEscape($_item->getName()) ?></a></p>
75
+ <?php echo $this->getPriceHtml($_item, true, '-related') ?>
76
+ <?php if ($this->helper('wishlist')->isAllow()) : ?>
77
+ <a href="<?php echo $this->getAddToWishlistUrl($_item) ?>" class="link-wishlist"><?php echo $this->__('Add to Wishlist') ?></a>
78
+ <?php endif; ?>
79
+ </div>
80
+ </div>
81
+ </li>
82
+ <?php endforeach ?>
83
+ </ol>
84
+ <script type="text/javascript">decorateList('block-related', 'none-recursive')</script>
85
+ </div>
86
+ <script type="text/javascript">
87
+ //<![CDATA[
88
+ $$('.related-checkbox').each(function(elem){
89
+ Event.observe(elem, 'click', addRelatedToProduct)
90
+ });
91
+
92
+ var relatedProductsCheckFlag = false;
93
+ function selectAllRelated(txt){
94
+ if (relatedProductsCheckFlag == false) {
95
+ $$('.related-checkbox').each(function(elem){
96
+ elem.checked = true;
97
+ });
98
+ relatedProductsCheckFlag = true;
99
+ txt.innerHTML="<?php echo $this->__('unselect all') ?>";
100
+ } else {
101
+ $$('.related-checkbox').each(function(elem){
102
+ elem.checked = false;
103
+ });
104
+ relatedProductsCheckFlag = false;
105
+ txt.innerHTML="<?php echo $this->__('select all') ?>";
106
+ }
107
+ addRelatedToProduct();
108
+ }
109
+
110
+ function addRelatedToProduct(){
111
+ var checkboxes = $$('.related-checkbox');
112
+ var values = [];
113
+ for(var i=0;i<checkboxes.length;i++){
114
+ if(checkboxes[i].checked) values.push(checkboxes[i].value);
115
+ }
116
+ if($('related-products-field')){
117
+ $('related-products-field').value = values.join(',');
118
+ }
119
+ }
120
+ //]]>
121
+ </script>
122
+ </div>
123
+ <?php endif ?>
app/design/frontend/base/default/template/recommend/catalog/product/list/upsell.phtml ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Academic Free License (AFL 3.0)
8
+ * that is bundled with this package in the file LICENSE_AFL.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/afl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category design
22
+ * @package base_default
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
25
+ */
26
+ ?>
27
+ <?php
28
+ if(count($this->getItemCollection()->getItems())):
29
+ $gaUA = Mage::helper('recommend')->getGoogleAccountId();
30
+ $toutType = $this->getRecommendationType();
31
+ $gafunction='';
32
+ if( strlen($gaUA) > 0 && Mage::helper('recommend')->getGoogleAccountEnabled()) {
33
+ ?>
34
+ <script type="text/javascript">
35
+ //<![CDATA[
36
+ (function() {
37
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
38
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
39
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
40
+ })();
41
+ var _gaq = _gaq || [];
42
+ _gaq.push(['_setAccount', '<?php echo $gaUA; ?>']);
43
+ //]]>
44
+ </script>
45
+ <?php
46
+ }
47
+ ?>
48
+ <div class="box-collateral box-up-sell">
49
+ <h2><?php echo $this->__('You may also be interested in the following product(s)') ?></h2>
50
+ <table class="products-grid" id="upsell-product-table">
51
+ <?php // $this->setColumnCount(5); // uncomment this line if you want to have another number of columns. also can be changed in layout ?>
52
+ <?php $this->resetItemsIterator() ?>
53
+ <?php for($_i=0;$_i<$this->getRowCount();$_i++): ?>
54
+ <tr>
55
+ <?php for($_j=0;$_j<$this->getColumnCount();$_j++): ?>
56
+ <?php if($_link=$this->getIterableItem()): ?>
57
+ <?php
58
+ if( strlen($gaUA) > 0 && Mage::helper('recommend')->getGoogleAccountEnabled()) {
59
+ $gafunction='onclick="_gaq.push([\'_trackEvent\', \'4TellRecs\', \''.$toutType.'\', \''.$_link->getEntityId().'\']);"';
60
+ }
61
+ ?>
62
+ <td>
63
+ <a href="<?php echo $_link->getProductUrl() ?>" <?php echo $gafunction; ?> title="<?php echo $this->htmlEscape($_link->getName()) ?>" class="product-image"><img src="<?php echo $this->helper('catalog/image')->init($_link, 'small_image')->resize(125) ?>" width="125" height="125" alt="<?php echo $this->htmlEscape($_link->getName()) ?>" /></a>
64
+ <h3 class="product-name"><a href="<?php echo $_link->getProductUrl() ?>" title="<?php echo $this->htmlEscape($_link->getName()) ?>"><?php echo $this->htmlEscape($_link->getName()) ?></a></h3>
65
+ <?php echo $this->getPriceHtml($_link, true, '-upsell') ?>
66
+ <?php echo $this->getReviewsSummaryHtml($_link) ?>
67
+ </td>
68
+ <?php else: ?>
69
+ <td class="empty">&nbsp;</td>
70
+ <?php endif; ?>
71
+ <?php endfor; ?>
72
+ </tr>
73
+ <?php endfor; ?>
74
+ </table>
75
+ <script type="text/javascript">decorateTable('upsell-product-table')</script>
76
+ </div>
77
+ <?php
78
+ endif
79
+ ?>
app/design/frontend/base/default/template/recommend/checkout/cart/crosssell.phtml ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Academic Free License (AFL 3.0)
8
+ * that is bundled with this package in the file LICENSE_AFL.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/afl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category design
22
+ * @package base_default
23
+ * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
25
+ */
26
+ ?>
27
+ <?php
28
+ /**
29
+ * Cart cross sell items template
30
+ *
31
+ * @see Mage_Checkout_Block_Cart_Crosssell
32
+ */
33
+ ?>
34
+ <?php
35
+ if($this->getItemCount()):
36
+ $gaUA = Mage::helper('recommend')->getGoogleAccountId();
37
+ $toutType = $this->getRecommendationType();
38
+ $gafunction='';
39
+ if( strlen($gaUA) > 0 && Mage::helper('recommend')->getGoogleAccountEnabled()) {
40
+ ?>
41
+ <script type="text/javascript">
42
+ //<![CDATA[
43
+ (function() {
44
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
45
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
46
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
47
+ })();
48
+ var _gaq = _gaq || [];
49
+ _gaq.push(['_setAccount', '<?php echo $gaUA; ?>']);
50
+ //]]>
51
+ </script>
52
+ <?php
53
+ }
54
+ ?>
55
+ <div class="crosssell">
56
+ <h2><?php echo $this->__('Based on your selection, you may be interested in the following items:') ?></h2>
57
+ <ul id="crosssell-products-list">
58
+ <?php foreach ($this->getItems() as $_item): ?>
59
+ <?php
60
+ if( strlen($gaUA) > 0 && Mage::helper('recommend')->getGoogleAccountEnabled()) {
61
+ $gafunction='onclick="_gaq.push([\'_trackEvent\', \'4TellRecs\', \''.$toutType.'\', \''.$_item->getEntityId().'\']);"';
62
+ }
63
+ ?>
64
+ <li class="item">
65
+ <a class="product-image" href="<?php echo $_item->getProductUrl() ?>" <?php echo $gafunction; ?> title="<?php echo $this->htmlEscape($_item->getName()) ?>"><img src="<?php echo $this->helper('catalog/image')->init($_item, 'thumbnail')->resize(75); ?>" width="75" height="75" alt="<?php echo $this->htmlEscape($_item->getName()) ?>" /></a>
66
+ <div class="product-details">
67
+ <h3 class="product-name"><a href="<?php echo $_item->getProductUrl() ?>" <?php echo $gafunction; ?>><?php echo $this->htmlEscape($_item->getName()) ?></a></h3>
68
+ <?php echo $this->getPriceHtml($_item, true) ?>
69
+ <button type="button" title="<?php echo $this->__('Add to Cart') ?>" class="button btn-cart" onclick="setLocation('<?php echo $this->getAddToCartUrl($_item) ?>')"><span><span><?php echo $this->__('Add to Cart') ?></span></span></button>
70
+ <ul class="add-to-links">
71
+ <?php if ($this->helper('wishlist')->isAllow()) : ?>
72
+ <li><a href="<?php echo $this->getAddToWishlistUrl($_item) ?>" class="link-wishlist"><?php echo $this->__('Add to Wishlist') ?></a></li>
73
+ <?php endif; ?>
74
+ <?php if($_compareUrl=$this->getAddToCompareUrl($_item)): ?>
75
+ <li><span class="separator">|</span> <a href="<?php echo $_compareUrl ?>" class="link-compare"><?php echo $this->__('Add to Compare') ?></a></li>
76
+ <?php endif; ?>
77
+ </ul>
78
+ </div>
79
+ </li>
80
+ <?php endforeach; ?>
81
+ </ul>
82
+ <script type="text/javascript">decorateList('crosssell-products-list', 'none-recursive')</script>
83
+ </div>
84
+ <?php
85
+ endif;
86
+ ?>
app/etc/modules/FourTell_Recommend.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <FourTell_Recommend>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </FourTell_Recommend>
8
+ </modules>
9
+ </config>
app/locale/en_US/FourTell_Recommend.csv ADDED
@@ -0,0 +1 @@
 
1
+ // this step is optional
js/jquery.tools.min.js ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery Tools v1.2.6 - The missing UI library for the Web
3
+ *
4
+ * overlay/overlay.js
5
+ * scrollable/scrollable.js
6
+ * tabs/tabs.js
7
+ * tooltip/tooltip.js
8
+ *
9
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
10
+ *
11
+ * http://flowplayer.org/tools/
12
+ *
13
+ */
14
+ /*! jQuery v1.6.4 http://jquery.com/ | http://jquery.org/license */
15
+ (function(a,b){function cu(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cr(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cq(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cp(){cn=b}function co(){setTimeout(cp,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bZ(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bY(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bA.test(a)?d(a,e):bY(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bY(a+"["+e+"]",b[e],c,d);else d(a,b)}function bX(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bW(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bP,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bW(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bW(a,c,d,e,"*",g));return l}function bV(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bL),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function by(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bt:bu;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bv(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bl(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bd,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bk(a){f.nodeName(a,"input")?bj(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bj)}function bj(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bi(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bh(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bg(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bf(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function V(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(Q.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function U(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function M(a,b){return(a&&a!=="*"?a+".":"")+b.replace(y,"`").replace(z,"&")}function L(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(w,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function J(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function D(){return!0}function C(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function K(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(K,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.4",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;B.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:H?function(a){return a==null?"":H.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?F.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(!b)return-1;if(I)return I.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=G.call(arguments,2),g=function(){return a.apply(c,f.concat(G.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){J["[object "+b+"]"]=b.toLowerCase()}),A=e.uaMatch(z),A.browser&&(e.browser[A.browser]=!0,e.browser.version=A.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?C=function(){c.removeEventListener("DOMContentLoaded",C,!1),e.ready()}:c.attachEvent&&(C=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",C),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),h&&f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g+"With"](this===b?d:this,[h])}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;a.setAttribute("className","t"),a.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-1000px",top:"-1000px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i=f.expando,j=typeof c=="string",k=a.nodeType,l=k?f.cache:a,m=k?a[f.expando]:a[f.expando]&&f.expando;if((!m||e&&m&&l[m]&&!l[m][i])&&j&&d===b)return;m||(k?a[f.expando]=m=++f.uuid:m=f.expando),l[m]||(l[m]={},k||(l[m].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?l[m][i]=f.extend(l[m][i],c):l[m]=f.extend(l[m],c);g=l[m],e&&(g[i]||(g[i]={}),g=g[i]),d!==b&&(g[f.camelCase(c)]=d);if(c==="events"&&!g[c])return g[i]&&g[i].events;j?(h=g[c],h==null&&(h=g[f.camelCase(c)])):h=g;return h}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e=f.expando,g=a.nodeType,h=g?f.cache:a,i=g?a[f.expando]:f.expando;if(!h[i])return;if(b){d=c?h[i][e]:h[i];if(d){d[b]||(b=f.camelCase(b)),delete d[b];if(!l(d))return}}if(c){delete h[i][e];if(!l(h[i]))return}var j=h[i][e];f.support.deleteExpando||!h.setInterval?delete h[i]:h[i]=null,j?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=j):g&&(f.support.deleteExpando?delete a[f.expando]:a.removeAttribute?a.removeAttribute(f.expando):a[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,l.done(m);m();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,u,v;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(o);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(o);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(n," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h<i;h++){var j=e[h];if(j.selected&&(f.support.optDisabled?!j.disabled:j.getAttribute("disabled")===null)&&(!j.parentNode.disabled||!f.nodeName(j.parentNode,"optgroup"))){b=f(j).val();if(g)return b;d.push(b)}}if(g&&!d.length&&e.length)return f(e[c]).val();return d},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=v:u&&(i=u)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.attr(a,b,""),a.removeAttribute(b),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(u&&f.nodeName(a,"button"))return u.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(u&&f.nodeName(a,"button"))return u.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==null?g:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabIndex=f.propHooks.tabIndex,v={get:function(a,c){var d;return f.prop(a,c)===!0||(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(u=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var w=/\.(.*)$/,x=/^(?:textarea|input|select)$/i,y=/\./g,z=/ /g,A=/[^\w\s.|`]/g,B=function(a){return a.replace(A,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=C;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=C);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),B).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete
16
+ t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,M(a.origType,a.selector),f.extend({},a,{handler:L,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,M(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?D:C):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=D;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=D;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=D,this.stopPropagation()},isDefaultPrevented:C,isPropagationStopped:C,isImmediatePropagationStopped:C};var E=function(a){var b=a.relatedTarget,c=!1,d=a.type;a.type=a.data,b!==this&&(b&&(c=f.contains(this,b)),c||(f.event.handle.apply(this,arguments),a.type=d))},F=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?F:E,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?F:E)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=f.nodeName(b,"input")||f.nodeName(b,"button")?b.type:"";(c==="submit"||c==="image")&&f(b).closest("form").length&&J("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=f.nodeName(b,"input")||f.nodeName(b,"button")?b.type:"";(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&J("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var G,H=function(a){var b=f.nodeName(a,"input")?a.type:"",c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},I=function(c){var d=c.target,e,g;if(!!x.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=H(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:I,beforedeactivate:I,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&I.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&I.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",H(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in G)f.event.add(this,c+".specialChange",G[c]);return x.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return x.test(this.nodeName)}},G=f.event.special.change.filters,G.focus=G.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var K={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||C,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=w.exec(h),k="",j&&(k=j[0],h=h.replace(w,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,K[h]?(a.push(K[h]+k),h=h+k):h=(K[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+M(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+M(h,m),e)}return this}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){if(a===b){g=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var N=/Until$/,O=/^(?:parents|prevUntil|prevAll)/,P=/,/,Q=/^.[^:#\[\.,]*$/,R=Array.prototype.slice,S=f.expr.match.POS,T={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(V(this,a,!1),"not",a)},filter:function(a){return this.pushStack(V(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=S.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=S.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(U(c[0])||U(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=R.call(arguments);N.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!T[a]?f.unique(e):e,(this.length>1||P.test(d))&&O.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|object|embed|option|style)/i,bb=/checked\s*(?:[^=]|=\s*.checked.)/i,bc=/\/(java|ecma)script/i,bd=/^\s*<!(?:\[CDATA\[|\-\-)/,be={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};be.optgroup=be.option,be.tbody=be.tfoot=be.colgroup=be.caption=be.thead,be.th=be.td,f.support.htmlSerialize||(be._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!be[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bb.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bf(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bl)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i;b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!ba.test(a[0])&&(f.support.checkClone||!bb.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean
17
+ (a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bh(a,d),e=bi(a),g=bi(d);for(h=0;e[h];++h)g[h]&&bh(e[h],g[h])}if(b){bg(a,d);if(c){e=bi(a),g=bi(d);for(h=0;e[h];++h)bg(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=be[l]||be._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bk(k[i]);else bk(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bc.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bm=/alpha\([^)]*\)/i,bn=/opacity=([^)]*)/,bo=/([A-Z]|^ms)/g,bp=/^-?\d+(?:px)?$/i,bq=/^-?\d/,br=/^([\-+])=([\-+.\de]+)/,bs={position:"absolute",visibility:"hidden",display:"block"},bt=["Left","Right"],bu=["Top","Bottom"],bv,bw,bx;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bv(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=br.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bv)return bv(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return by(a,b,d);f.swap(a,bs,function(){e=by(a,b,d)});return e}},set:function(a,b){if(!bp.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bn.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bm,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bm.test(g)?g.replace(bm,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bv(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bw=function(a,c){var d,e,g;c=c.replace(bo,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bx=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bp.test(d)&&bq.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bv=bw||bx,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bz=/%20/g,bA=/\[\]$/,bB=/\r?\n/g,bC=/#.*$/,bD=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bE=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bF=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bG=/^(?:GET|HEAD)$/,bH=/^\/\//,bI=/\?/,bJ=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bK=/^(?:select|textarea)/i,bL=/\s+/,bM=/([?&])_=[^&]*/,bN=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bO=f.fn.load,bP={},bQ={},bR,bS,bT=["*/"]+["*"];try{bR=e.href}catch(bU){bR=c.createElement("a"),bR.href="",bR=bR.href}bS=bN.exec(bR.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bO)return bO.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bJ,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bK.test(this.nodeName)||bE.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bB,"\r\n")}}):{name:b.name,value:c.replace(bB,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?bX(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),bX(a,b);return a},ajaxSettings:{url:bR,isLocal:bF.test(bS[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bT},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bV(bP),ajaxTransport:bV(bQ),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?bZ(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=b$(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bD.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bC,"").replace(bH,bS[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bL),d.crossDomain==null&&(r=bN.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bS[1]&&r[2]==bS[2]&&(r[3]||(r[1]==="http:"?80:443))==(bS[3]||(bS[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bW(bP,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bG.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bI.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bM,"$1_="+x);d.url=y+(y===d.url?(bI.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bT+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bW(bQ,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bz,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cq("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cr(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cq("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cq("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=cr(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block"))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],cj.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=ck.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[i]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cq("show",1),slideUp:cq("hide",1),slideToggle:cq("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return d.step(a)}var d=this,e=f.fx;this.startTime=cn||co(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&f.timers.push(g)&&!cl&&(cl=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=cn||co(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b<a.length;++b)a[b]()||a.splice(b--,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cl),cl=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cs=/^t(?:able|d|h)$/i,ct=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cu(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!cs.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=ct.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!ct.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cu(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cu(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNaN(j)?i:j}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window);(function(a){a.tools=a.tools||{version:"v1.2.6"},a.tools.overlay={addEffect:function(a,b,d){c[a]=[b,d]},conf:{close:null,closeOnClick:!0,closeOnEsc:!0,closeSpeed:"fast",effect:"default",fixed:!a.browser.msie||a.browser.version>6,left:"center",load:!1,mask:null,oneInstance:!0,speed:"normal",target:null,top:"10%"}};var b=[],c={};a.tools.overlay.addEffect("default",function(b,c){var d=this.getConf(),e=a(window);d.fixed||(b.top+=e.scrollTop(),b.left+=e.scrollLeft()),b.position=d.fixed?"fixed":"absolute",this.getOverlay().css(b).fadeIn(d.speed,c)},function(a){this.getOverlay().fadeOut(this.getConf().closeSpeed,a)});function d(d,e){var f=this,g=d.add(f),h=a(window),i,j,k,l=a.tools.expose&&(e.mask||e.expose),m=Math.random().toString().slice(10);l&&(typeof l=="string"&&(l={color:l}),l.closeOnClick=l.closeOnEsc=!1);var n=e.target||d.attr("rel");j=n?a(n):null||d;if(!j.length)throw"Could not find Overlay: "+n;d&&d.index(j)==-1&&d.click(function(a){f.load(a);return a.preventDefault()}),a.extend(f,{load:function(d){if(f.isOpened())return f;var i=c[e.effect];if(!i)throw"Overlay: cannot find effect : \""+e.effect+"\"";e.oneInstance&&a.each(b,function(){this.close(d)}),d=d||a.Event(),d.type="onBeforeLoad",g.trigger(d);if(d.isDefaultPrevented())return f;k=!0,l&&a(j).expose(l);var n=e.top,o=e.left,p=j.outerWidth({margin:!0}),q=j.outerHeight({margin:!0});typeof n=="string"&&(n=n=="center"?Math.max((h.height()-q)/2,0):parseInt(n,10)/100*h.height()),o=="center"&&(o=Math.max((h.width()-p)/2,0)),i[0].call(f,{top:n,left:o},function(){k&&(d.type="onLoad",g.trigger(d))}),l&&e.closeOnClick&&a.mask.getMask().one("click",f.close),e.closeOnClick&&a(document).bind("click."+m,function(b){a(b.target).parents(j).length||f.close(b)}),e.closeOnEsc&&a(document).bind("keydown."+m,function(a){a.keyCode==27&&f.close(a)});return f},close:function(b){if(!f.isOpened())return f;b=b||a.Event(),b.type="onBeforeClose",g.trigger(b);if(!b.isDefaultPrevented()){k=!1,c[e.effect][1].call(f,function(){b.type="onClose",g.trigger(b)}),a(document).unbind("click."+m).unbind("keydown."+m),l&&a.mask.close();return f}},getOverlay:function(){return j},getTrigger:function(){return d},getClosers:function(){return i},isOpened:function(){return k},getConf:function(){return e}}),a.each("onBeforeLoad,onStart,onLoad,onBeforeClose,onClose".split(","),function(b,c){a.isFunction(e[c])&&a(f).bind(c,e[c]),f[c]=function(b){b&&a(f).bind(c,b);return f}}),i=j.find(e.close||".close"),!i.length&&!e.close&&(i=a("<a class=\"close\"></a>"),j.prepend(i)),i.click(function(a){f.close(a)}),e.load&&f.load()}a.fn.overlay=function(c){var e=this.data("overlay");if(e)return e;a.isFunction(c)&&(c={onBeforeLoad:c}),c=a.extend(!0,{},a.tools.overlay.conf,c),this.each(function(){e=new d(a(this),c),b.push(e),a(this).data("overlay",e)});return c.api?e:this}})(jQuery);
18
+ (function(a){a.tools=a.tools||{version:"v1.2.6"},a.tools.scrollable={conf:{activeClass:"active",circular:!1,clonedClass:"cloned",disabledClass:"disabled",easing:"swing",initialIndex:0,item:"> *",items:".items",keyboard:!0,mousewheel:!1,next:".next",prev:".prev",size:1,speed:400,vertical:!1,touch:!0,wheelSpeed:0}};function b(a,b){var c=parseInt(a.css(b),10);if(c)return c;var d=a[0].currentStyle;return d&&d.width&&parseInt(d.width,10)}function c(b,c){var d=a(c);return d.length<2?d:b.parent().find(c)}var d;function e(b,e){var f=this,g=b.add(f),h=b.children(),i=0,j=e.vertical;d||(d=f),h.length>1&&(h=a(e.items,b)),e.size>1&&(e.circular=!1),a.extend(f,{getConf:function(){return e},getIndex:function(){return i},getSize:function(){return f.getItems().size()},getNaviButtons:function(){return n.add(o)},getRoot:function(){return b},getItemWrap:function(){return h},getItems:function(){return h.find(e.item).not("."+e.clonedClass)},move:function(a,b){return f.seekTo(i+a,b)},next:function(a){return f.move(e.size,a)},prev:function(a){return f.move(-e.size,a)},begin:function(a){return f.seekTo(0,a)},end:function(a){return f.seekTo(f.getSize()-1,a)},focus:function(){d=f;return f},addItem:function(b){b=a(b),e.circular?(h.children().last().before(b),h.children().first().replaceWith(b.clone().addClass(e.clonedClass))):(h.append(b),o.removeClass("disabled")),g.trigger("onAddItem",[b]);return f},seekTo:function(b,c,k){b.jquery||(b*=1);if(e.circular&&b===0&&i==-1&&c!==0)return f;if(!e.circular&&b<0||b>f.getSize()||b<-1)return f;var l=b;b.jquery?b=f.getItems().index(b):l=f.getItems().eq(b);var m=a.Event("onBeforeSeek");if(!k){g.trigger(m,[b,c]);if(m.isDefaultPrevented()||!l.length)return f}var n=j?{top:-l.position().top}:{left:-l.position().left};i=b,d=f,c===undefined&&(c=e.speed),h.animate(n,c,e.easing,k||function(){g.trigger("onSeek",[b])});return f}}),a.each(["onBeforeSeek","onSeek","onAddItem"],function(b,c){a.isFunction(e[c])&&a(f).bind(c,e[c]),f[c]=function(b){b&&a(f).bind(c,b);return f}});if(e.circular){var k=f.getItems().slice(-1).clone().prependTo(h),l=f.getItems().eq(1).clone().appendTo(h);k.add(l).addClass(e.clonedClass),f.onBeforeSeek(function(a,b,c){if(!a.isDefaultPrevented()){if(b==-1){f.seekTo(k,c,function(){f.end(0)});return a.preventDefault()}b==f.getSize()&&f.seekTo(l,c,function(){f.begin(0)})}});var m=b.parents().add(b).filter(function(){if(a(this).css("display")==="none")return!0});m.length?(m.show(),f.seekTo(0,0,function(){}),m.hide()):f.seekTo(0,0,function(){})}var n=c(b,e.prev).click(function(a){a.stopPropagation(),f.prev()}),o=c(b,e.next).click(function(a){a.stopPropagation(),f.next()});e.circular||(f.onBeforeSeek(function(a,b){setTimeout(function(){a.isDefaultPrevented()||(n.toggleClass(e.disabledClass,b<=0),o.toggleClass(e.disabledClass,b>=f.getSize()-1))},1)}),e.initialIndex||n.addClass(e.disabledClass)),f.getSize()<2&&n.add(o).addClass(e.disabledClass),e.mousewheel&&a.fn.mousewheel&&b.mousewheel(function(a,b){if(e.mousewheel){f.move(b<0?1:-1,e.wheelSpeed||50);return!1}});if(e.touch){var p={};h[0].ontouchstart=function(a){var b=a.touches[0];p.x=b.clientX,p.y=b.clientY},h[0].ontouchmove=function(a){if(a.touches.length==1&&!h.is(":animated")){var b=a.touches[0],c=p.x-b.clientX,d=p.y-b.clientY;f[j&&d>0||!j&&c>0?"next":"prev"](),a.preventDefault()}}}e.keyboard&&a(document).bind("keydown.scrollable",function(b){if(!(!e.keyboard||b.altKey||b.ctrlKey||b.metaKey||a(b.target).is(":input"))){if(e.keyboard!="static"&&d!=f)return;var c=b.keyCode;if(j&&(c==38||c==40)){f.move(c==38?-1:1);return b.preventDefault()}if(!j&&(c==37||c==39)){f.move(c==37?-1:1);return b.preventDefault()}}}),e.initialIndex&&f.seekTo(e.initialIndex,0,function(){})}a.fn.scrollable=function(b){var c=this.data("scrollable");if(c)return c;b=a.extend({},a.tools.scrollable.conf,b),this.each(function(){c=new e(a(this),b),a(this).data("scrollable",c)});return b.api?c:this}})(jQuery);
19
+ (function(a){a.tools=a.tools||{version:"v1.2.6"},a.tools.tabs={conf:{tabs:"a",current:"current",onBeforeClick:null,onClick:null,effect:"default",initialIndex:0,event:"click",rotate:!1,slideUpSpeed:400,slideDownSpeed:400,history:!1},addEffect:function(a,c){b[a]=c}};var b={"default":function(a,b){this.getPanes().hide().eq(a).show(),b.call()},fade:function(a,b){var c=this.getConf(),d=c.fadeOutSpeed,e=this.getPanes();d?e.fadeOut(d):e.hide(),e.eq(a).fadeIn(c.fadeInSpeed,b)},slide:function(a,b){var c=this.getConf();this.getPanes().slideUp(c.slideUpSpeed),this.getPanes().eq(a).slideDown(c.slideDownSpeed,b)},ajax:function(a,b){this.getPanes().eq(0).load(this.getTabs().eq(a).attr("href"),b)}},c,d;a.tools.tabs.addEffect("horizontal",function(b,e){if(!c){var f=this.getPanes().eq(b),g=this.getCurrentPane();d||(d=this.getPanes().eq(0).width()),c=!0,f.show(),g.animate({width:0},{step:function(a){f.css("width",d-a)},complete:function(){a(this).hide(),e.call(),c=!1}}),g.length||(e.call(),c=!1)}});function e(c,d,e){var f=this,g=c.add(this),h=c.find(e.tabs),i=d.jquery?d:c.children(d),j;h.length||(h=c.children()),i.length||(i=c.parent().find(d)),i.length||(i=a(d)),a.extend(this,{click:function(c,d){var i=h.eq(c);typeof c=="string"&&c.replace("#","")&&(i=h.filter("[href*="+c.replace("#","")+"]"),c=Math.max(h.index(i),0));if(e.rotate){var k=h.length-1;if(c<0)return f.click(k,d);if(c>k)return f.click(0,d)}if(!i.length){if(j>=0)return f;c=e.initialIndex,i=h.eq(c)}if(c===j)return f;d=d||a.Event(),d.type="onBeforeClick",g.trigger(d,[c]);if(!d.isDefaultPrevented()){b[e.effect].call(f,c,function(){j=c,d.type="onClick",g.trigger(d,[c])}),h.removeClass(e.current),i.addClass(e.current);return f}},getConf:function(){return e},getTabs:function(){return h},getPanes:function(){return i},getCurrentPane:function(){return i.eq(j)},getCurrentTab:function(){return h.eq(j)},getIndex:function(){return j},next:function(){return f.click(j+1)},prev:function(){return f.click(j-1)},destroy:function(){h.unbind(e.event).removeClass(e.current),i.find("a[href^=#]").unbind("click.T");return f}}),a.each("onBeforeClick,onClick".split(","),function(b,c){a.isFunction(e[c])&&a(f).bind(c,e[c]),f[c]=function(b){b&&a(f).bind(c,b);return f}}),e.history&&a.fn.history&&(a.tools.history.init(h),e.event="history"),h.each(function(b){a(this).bind(e.event,function(a){f.click(b,a);return a.preventDefault()})}),i.find("a[href^=#]").bind("click.T",function(b){f.click(a(this).attr("href"),b)}),location.hash&&e.tabs=="a"&&c.find("[href="+location.hash+"]").length?f.click(location.hash):(e.initialIndex===0||e.initialIndex>0)&&f.click(e.initialIndex)}a.fn.tabs=function(b,c){var d=this.data("tabs");d&&(d.destroy(),this.removeData("tabs")),a.isFunction(c)&&(c={onBeforeClick:c}),c=a.extend({},a.tools.tabs.conf,c),this.each(function(){d=new e(a(this),b,c),a(this).data("tabs",d)});return c.api?d:this}})(jQuery);
20
+ (function(a){a.tools=a.tools||{version:"v1.2.6"},a.tools.tooltip={conf:{effect:"toggle",fadeOutSpeed:"fast",predelay:0,delay:30,opacity:1,tip:0,fadeIE:!1,position:["top","center"],offset:[0,0],relative:!1,cancelDefault:!0,events:{def:"mouseenter,mouseleave",input:"focus,blur",widget:"focus mouseenter,blur mouseleave",tooltip:"mouseenter,mouseleave"},layout:"<div/>",tipClass:"tooltip"},addEffect:function(a,c,d){b[a]=[c,d]}};var b={toggle:[function(a){var b=this.getConf(),c=this.getTip(),d=b.opacity;d<1&&c.css({opacity:d}),c.show(),a.call()},function(a){this.getTip().hide(),a.call()}],fade:[function(b){var c=this.getConf();!a.browser.msie||c.fadeIE?this.getTip().fadeTo(c.fadeInSpeed,c.opacity,b):(this.getTip().show(),b())},function(b){var c=this.getConf();!a.browser.msie||c.fadeIE?this.getTip().fadeOut(c.fadeOutSpeed,b):(this.getTip().hide(),b())}]};function c(b,c,d){var e=d.relative?b.position().top:b.offset().top,f=d.relative?b.position().left:b.offset().left,g=d.position[0];e-=c.outerHeight()-d.offset[0],f+=b.outerWidth()+d.offset[1],/iPad/i.test(navigator.userAgent)&&(e-=a(window).scrollTop());var h=c.outerHeight()+b.outerHeight();g=="center"&&(e+=h/2),g=="bottom"&&(e+=h),g=d.position[1];var i=c.outerWidth()+b.outerWidth();g=="center"&&(f-=i/2),g=="left"&&(f-=i);return{top:e,left:f}}function d(d,e){var f=this,g=d.add(f),h,i=0,j=0,k=d.attr("title"),l=d.attr("data-tooltip"),m=b[e.effect],n,o=d.is(":input"),p=o&&d.is(":checkbox, :radio, select, :button, :submit"),q=d.attr("type"),r=e.events[q]||e.events[o?p?"widget":"input":"def"];if(!m)throw"Nonexistent effect \""+e.effect+"\"";r=r.split(/,\s*/);if(r.length!=2)throw"Tooltip: bad events configuration for "+q;d.bind(r[0],function(a){clearTimeout(i),e.predelay?j=setTimeout(function(){f.show(a)},e.predelay):f.show(a)}).bind(r[1],function(a){clearTimeout(j),e.delay?i=setTimeout(function(){f.hide(a)},e.delay):f.hide(a)}),k&&e.cancelDefault&&(d.removeAttr("title"),d.data("title",k)),a.extend(f,{show:function(b){if(!h){l?h=a(l):e.tip?h=a(e.tip).eq(0):k?h=a(e.layout).addClass(e.tipClass).appendTo(document.body).hide().append(k):(h=d.next(),h.length||(h=d.parent().next()));if(!h.length)throw"Cannot find tooltip for "+d}if(f.isShown())return f;h.stop(!0,!0);var o=c(d,h,e);e.tip&&h.html(d.data("title")),b=a.Event(),b.type="onBeforeShow",g.trigger(b,[o]);if(b.isDefaultPrevented())return f;o=c(d,h,e),h.css({position:"absolute",top:o.top,left:o.left}),n=!0,m[0].call(f,function(){b.type="onShow",n="full",g.trigger(b)});var p=e.events.tooltip.split(/,\s*/);h.data("__set")||(h.unbind(p[0]).bind(p[0],function(){clearTimeout(i),clearTimeout(j)}),p[1]&&!d.is("input:not(:checkbox, :radio), textarea")&&h.unbind(p[1]).bind(p[1],function(a){a.relatedTarget!=d[0]&&d.trigger(r[1].split(" ")[0])}),e.tip||h.data("__set",!0));return f},hide:function(c){if(!h||!f.isShown())return f;c=a.Event(),c.type="onBeforeHide",g.trigger(c);if(!c.isDefaultPrevented()){n=!1,b[e.effect][1].call(f,function(){c.type="onHide",g.trigger(c)});return f}},isShown:function(a){return a?n=="full":n},getConf:function(){return e},getTip:function(){return h},getTrigger:function(){return d}}),a.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","),function(b,c){a.isFunction(e[c])&&a(f).bind(c,e[c]),f[c]=function(b){b&&a(f).bind(c,b);return f}})}a.fn.tooltip=function(b){var c=this.data("tooltip");if(c)return c;b=a.extend(!0,{},a.tools.tooltip.conf,b),typeof b.position=="string"&&(b.position=b.position.split(/,?\s/)),this.each(function(){c=new d(a(this),b),a(this).data("tooltip",c)});return b.api?c:this}})(jQuery);
21
+ jQuery.noConflict();
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>FourTell_Recommend_S</name>
4
+ <version>1.2.8</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://www.opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Extension to connect to 4-Tell product recommendation services</summary>
10
+ <description>Extension to connect to 4-Tell product recommendation services</description>
11
+ <notes>Google Analytics code for related, crosssells, upsells</notes>
12
+ <authors><author><name>4Tell Inc</name><user>nlofgren</user><email>neil@4-tell.com</email></author></authors>
13
+ <date>2012-02-17</date>
14
+ <time>2012-02-17</time>
15
+ <contents><target name="magecommunity"><dir name="FourTell"><dir name="Recommend"><dir><dir name="Block"><dir name="Catalog"><dir name="Product"><dir name="List"><file name="Related.php" hash="05e53235542d67eef22c5db524421529"/><file name="Upsell.php" hash="a5e9ec301b6fbf6c2e72f65c7241b1a1"/></dir></dir></dir><dir name="Checkout"><dir name="Cart"><file name="Crosssell.php" hash="58eea7eb123df498f33cd33db46021a4"/><file name="Crosssellcommunity.php" hash="4a186abc5c99736b91984577357e33f5"/><file name="Crosssellenterprise.php" hash="08833d8b155fcd0490dc100eca549e9f"/></dir></dir><dir name="System"><dir name="Config"><dir name="Form"><dir name="Field"><file name="Upload.php" hash="052c32eeb22437ef2c9291f60d6dad23"/></dir></dir><dir name="Google"><dir name="Analytics"><file name="Change.php" hash="9ef76b1efeab5316d790e7b07305565c"/><file name="Code.php" hash="d412c7bbcf6d6c5a540c66a70e26ea1d"/><file name="Status.php" hash="78f2a402c8c3c61c79e510b3d9251608"/></dir></dir></dir></dir><file name="Widget.php" hash="4ad115559597f0d69058101483c200e4"/></dir><dir name="Helper"><file name="Data.php" hash="5859427c6bbe4da27d24c2eb5e4737ae"/></dir><dir name="Model"><dir name="Adminhtml"><file name="Uploadform.php" hash="aeeb0d8dfb15d2ca3e83bf343f0c8e8f"/></dir><file name="DataProcess.php" hash="bf42a3cb1489579f13b86fd913f3dee5"/><file name="Observer.php" hash="6e64e578a37e102d3d7868cee2520976"/><dir name="System"><dir name="Config"><dir name="Source"><file name="Crosssell.php" hash="7e17ad93818ea3ee31ac4bf4c858586c"/><file name="Enabled.php" hash="de9c787170a8cdc02763e65ddb24cf01"/><file name="Mode.php" hash="f830de55a2223e112010d98b1999dee9"/><file name="Related.php" hash="94101116d67f4f0a93dfd66ac6ef4c66"/><file name="Reportlevel.php" hash="5fae778206574824df9f29aa8b72a816"/><file name="Resell.php" hash="7c8e551665aad76692e98349001a1d2d"/><file name="Resultformat.php" hash="9d593961a2c6b846f8092441403c0add"/><file name="Upsell.php" hash="9acd8b40e00c60816187b03beadae0f4"/></dir><dir name="Validation"><file name="Clientid.php" hash="9fd28852ad88b253c20e06bc3e6dd108"/><file name="Email.php" hash="a9c8e7759e9efdba4624e24df8496e3e"/><file name="Maxsalesdataageinmonths.php" hash="7e0c4f922f6ad2a3047bc296cf64efed"/><file name="Mincommon.php" hash="a79c18f3f04c6031976f00aa6ba86f3f"/><file name="Minlikelihood.php" hash="281ba400ca276b66617c928effc9713d"/><file name="Numcrosssell.php" hash="a767518242e2829b0ffa9224b8b2fd09"/><file name="Numrelated.php" hash="23bab32136ce4825d71d0bb755386ae5"/><file name="Numupsell.php" hash="badbe34d0781b7239829a7d76d37d078"/><file name="Owner.php" hash="3c1569fb2ea1300665447efef6f75123"/></dir></dir></dir><dir name="Upload"><file name="Frequency.php" hash="46f8352ca038ac089e58803f93f375ec"/></dir><file name="UploadSetting.php" hash="128b0542e004ab142099727e19230034"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="UploadformController.php" hash="62837b3fb96296bb892aea6290384411"/></dir></dir><dir name="etc"><file name="config.xml" hash="e11c61a531295115b1ea90dac035b4fd"/><file name="system.xml" hash="ca678935196450f96a2de93c9e70828b"/><file name="upload.xml" hash="cb681f67371e8bb70c344b06077a8c1b"/><file name="widget.xml" hash="419db35eacdc29efeb5ab1a16f6fcaed"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="recommend.xml" hash="31c1d9505e1e75e4a84bfc8a57dbe770"/></dir><dir name="template"><dir name="recommend"><file name="uploadform.phtml" hash="75076ab74754925df55355803853d06f"/></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="recommend.xml" hash="7476d838384550b7303de09562d832db"/></dir><dir name="template"><dir name="recommend"><dir><dir name="catalog"><dir name="product"><dir name="list"><file name="related.phtml" hash="85607d036396a9676df287198d0337ed"/><file name="upsell.phtml" hash="81f56a44037d8d468f4b7b07358aa746"/></dir></dir></dir><dir name="checkout"><dir name="cart"><file name="crosssell.phtml" hash="2b0a9b82b82d1e901f85e07a1f3ef86f"/></dir></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="FourTell_Recommend.xml" hash="868f2cca675ef1190b6a73de52469f9f"/></dir></target><target name="magelocale"><dir name="en_US"><file name="FourTell_Recommend.csv" hash="e8b3a2f7139dbe184779cec31ae2d68a"/></dir></target><target name="mageskin"><dir name="frontend"><dir name="base"><dir name="default"><dir name="images"><file name="hori_large.png" hash="259708c04db8e05f9cc1f732cce78523"/></dir></dir></dir></dir></target><target name="mageweb"><dir name="js"><file name="jquery.tools.min.js" hash="eec050dadaa6b202aa84ac85294af744"/></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.0.0</min><max>5.4.0</max></php></required></dependencies>
18
+ </package>
skin/frontend/base/default/images/hori_large.png ADDED
Binary file