indabox - Version 1.0.1.1

Version Notes

First stable release

Download this release

Release Info

Developer IndaBox
Extension indabox
Version 1.0.1.1
Comparing to
See all releases


Version 1.0.1.1

Files changed (36) hide show
  1. app/code/local/IndaBox/StorePickup/Block/Adminhtml/Configuration/Manual.php +13 -0
  2. app/code/local/IndaBox/StorePickup/Block/Checkout/Onepage/Payment/Methods.php +39 -0
  3. app/code/local/IndaBox/StorePickup/Block/Checkout/Onepage/Shipping/Method/Available.php +46 -0
  4. app/code/local/IndaBox/StorePickup/Block/Map.php +44 -0
  5. app/code/local/IndaBox/StorePickup/Exception.php +5 -0
  6. app/code/local/IndaBox/StorePickup/Helper/Config.php +86 -0
  7. app/code/local/IndaBox/StorePickup/Helper/Data.php +78 -0
  8. app/code/local/IndaBox/StorePickup/Model/Api.php +152 -0
  9. app/code/local/IndaBox/StorePickup/Model/Carrier/Storepickup.php +145 -0
  10. app/code/local/IndaBox/StorePickup/Model/GoogleMaps.php +69 -0
  11. app/code/local/IndaBox/StorePickup/Model/Observer.php +105 -0
  12. app/code/local/IndaBox/StorePickup/Model/Order/Point.php +27 -0
  13. app/code/local/IndaBox/StorePickup/Model/Point.php +135 -0
  14. app/code/local/IndaBox/StorePickup/Model/Points.php +58 -0
  15. app/code/local/IndaBox/StorePickup/Model/Resource/Order/Point.php +9 -0
  16. app/code/local/IndaBox/StorePickup/Model/Resource/Order/Point/Collection.php +10 -0
  17. app/code/local/IndaBox/StorePickup/Model/Source/Payment.php +21 -0
  18. app/code/local/IndaBox/StorePickup/Model/Source/Selectorpayment.php +12 -0
  19. app/code/local/IndaBox/StorePickup/controllers/IndexController.php +79 -0
  20. app/code/local/IndaBox/StorePickup/etc/config.xml +171 -0
  21. app/code/local/IndaBox/StorePickup/etc/jstranslator.xml +21 -0
  22. app/code/local/IndaBox/StorePickup/etc/system.xml +170 -0
  23. app/code/local/IndaBox/StorePickup/sql/ibstorepickup_setup/install-0.1.0.php +61 -0
  24. app/design/frontend/base/default/layout/ibstorepickup.xml +29 -0
  25. app/design/frontend/base/default/template/ibstorepickup/checkout/onepage/billing.phtml +246 -0
  26. app/design/frontend/base/default/template/ibstorepickup/map.phtml +79 -0
  27. app/etc/modules/IndaBox_StorePickup.xml +9 -0
  28. app/locale/en_US/IndaBox_StorePickup.csv +38 -0
  29. app/locale/it_IT/IndaBox_StorePickup.csv +38 -0
  30. js/indabox/markerclusterer.js +1310 -0
  31. js/indabox/storepickup.js +254 -0
  32. media/indabox/marker_location.png +0 -0
  33. media/indabox/marker_point.png +0 -0
  34. package.xml +18 -0
  35. skin/frontend/base/default/indabox/css/storepickup.css +61 -0
  36. skin/frontend/base/default/indabox/images/opc-ajax-loader.gif +0 -0
app/code/local/IndaBox/StorePickup/Block/Adminhtml/Configuration/Manual.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Block_Adminhtml_Configuration_Manual extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+
6
+ public function render(Varien_Data_Form_Element_Abstract $element)
7
+ {
8
+ $message = $this->__('For all the information on installing and configuring the module and IndaBox Merchant Account, you can refer to <a href="%s" target="_blank">this page</a>', 'https://www.indabox.it/magentomodule.html');
9
+ $html = '<td class="label" colspan="4">' . $message . '</td>';
10
+ return $this->_decorateRowHtml($element, $html);
11
+ }
12
+
13
+ }
app/code/local/IndaBox/StorePickup/Block/Checkout/Onepage/Payment/Methods.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Block_Checkout_Onepage_Payment_Methods extends Mage_Checkout_Block_Onepage_Payment_Methods
4
+ {
5
+ public function getMethods()
6
+ {
7
+ $methods = $this->getData('methods');
8
+ if (is_null($methods))
9
+ {
10
+ $store = $this->getQuote() ? $this->getQuote()->getStoreId() : null;
11
+ $methods = $this->helper('payment')->getStoreMethods($store, $this->getQuote());
12
+ foreach ($methods as $key => $method) {
13
+ if ($this->_canUseMethod($method)) {
14
+ $this->_assignMethod($method);
15
+ }
16
+ else {
17
+ unset($methods[$key]);
18
+ }
19
+ }
20
+
21
+ if ($this->getQuote()->getShippingAddress()->getShippingMethod() === 'ibstorepickup_ibstorepickup'
22
+ && Mage::helper('ibstorepickup/config')->getAllowSpecificPayments()
23
+ )
24
+ {
25
+ $allowed = array_flip(Mage::helper('ibstorepickup/config')->getSpecificPayments());
26
+ foreach ($methods as $key => $method)
27
+ {
28
+ if ( ! isset($allowed[$method->getCode()]))
29
+ unset($methods[$key]);
30
+ }
31
+
32
+ $methods = array_values($methods);
33
+ }
34
+
35
+ $this->setData('methods', $methods);
36
+ }
37
+ return $methods;
38
+ }
39
+ }
app/code/local/IndaBox/StorePickup/Block/Checkout/Onepage/Shipping/Method/Available.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Block_Checkout_Onepage_Shipping_Method_Available extends Mage_Checkout_Block_Onepage_Shipping_Method_Available {
4
+
5
+ protected $_storePickupAvailable = false;
6
+
7
+ protected function _filterGroups($groups)
8
+ {
9
+ $useStorePickup = Mage::helper('ibstorepickup')->getUseMethod();
10
+ foreach ($groups as $group => $rates)
11
+ {
12
+ if ($group === 'ibstorepickup' && $useStorePickup)
13
+ $this->_storePickupAvailable = true;
14
+
15
+ if ($group !== 'ibstorepickup' && $useStorePickup
16
+ || $group === 'ibstorepickup' && ! $useStorePickup
17
+ )
18
+ unset($groups[$group]);
19
+ }
20
+
21
+ return $groups;
22
+ }
23
+
24
+ public function getShippingRates()
25
+ {
26
+ if (empty($this->_rates)) {
27
+ $this->getAddress()->collectShippingRates()->save();
28
+
29
+ $groups = $this->getAddress()->getGroupedAllShippingRates();
30
+
31
+ $this->_rates = $this->_filterGroups($groups);
32
+ }
33
+
34
+ return $this->_rates;
35
+ }
36
+
37
+ protected function _afterToHtml($html)
38
+ {
39
+ if ($this->_storePickupAvailable)
40
+ $html .= $this->getLayout()->createBlock('ibstorepickup/map')->toHtml();
41
+
42
+ return $html;
43
+ }
44
+
45
+ }
46
+
app/code/local/IndaBox/StorePickup/Block/Map.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Block_Map extends Mage_Core_Block_Template
4
+ {
5
+
6
+ public function __construct()
7
+ {
8
+ parent::__construct();
9
+
10
+ $this->setTemplate('ibstorepickup/map.phtml');
11
+ }
12
+
13
+ public function getTosUrl()
14
+ {
15
+ return Mage::helper('ibstorepickup/config')->getTosUrl();
16
+ }
17
+
18
+ public function getGoogleMaps($callback = null)
19
+ {
20
+ $params = array(
21
+ 'v' => '3.exp',
22
+ 'region' => 'it',
23
+ );
24
+ if ($apiKey = Mage::helper('ibstorepickup/config')->getGMapsKey())
25
+ $params['key'] = $apiKey;
26
+ if ($callback)
27
+ $params['callback'] = $callback;
28
+ return 'https://maps.googleapis.com/maps/api/js?' . http_build_query($params);
29
+ }
30
+
31
+ public function getBillingAddress()
32
+ {
33
+ $address = Mage::getSingleton('checkout/session')->getQuote()->getBillingAddress();
34
+ $street = $address->getStreet();
35
+ $parts = array(
36
+ (is_array($street) ? implode(', ', $street) : $street) . ',',
37
+ $address->getPostcode(),
38
+ $address->getCity(),
39
+ );
40
+
41
+ return implode(' ', $parts);
42
+ }
43
+
44
+ }
app/code/local/IndaBox/StorePickup/Exception.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Exception extends Mage_Core_Exception {
4
+
5
+ }
app/code/local/IndaBox/StorePickup/Helper/Config.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Helper_Config extends Mage_Core_Helper_Abstract
4
+ {
5
+
6
+ const API_VERSION = '0.9.3';
7
+ const API_VERSION_HACK = '0.9';
8
+
9
+ const ENDPOINT_PRODUCTION = 'https://www.indabox.it/api/v:api_version/:api_method.php';
10
+ const ENDPOINT_SANDBOX = 'https://www.indabox.it/sandbox/v:api_version/:api_method.php';
11
+
12
+ const XML_PATH_ACCEPT = 'carriers/ibstorepickup/accept';
13
+ const XML_PATH_COST = 'carriers/ibstorepickup/cost';
14
+ const XML_PATH_SANDBOX = 'carriers/ibstorepickup/sandbox';
15
+ const XML_PATH_DEBUG = 'carriers/ibstorepickup/debug';
16
+ const XML_PATH_MERCHANTID = 'carriers/ibstorepickup/merchant_id';
17
+ const XML_PATH_APIKEY = 'carriers/ibstorepickup/api_key';
18
+ const XML_PATH_TOSURL = 'carriers/ibstorepickup/tos_url';
19
+ const XML_PATH_GMAPSKEY = 'carriers/ibstorepickup/gmaps_key';
20
+ const XML_PATH_ALLOWSPECIFIC = 'carriers/ibstorepickup/allowspecific_payment';
21
+ const XML_PATH_SPECIFICPAYMENTS = 'carriers/ibstorepickup/specificpayment';
22
+
23
+ public function getTosAccepted()
24
+ {
25
+ return Mage::getStoreConfigFlag(self::XML_PATH_ACCEPT);
26
+ }
27
+
28
+ public function getMerchantId()
29
+ {
30
+ return (string) Mage::getStoreConfig(self::XML_PATH_MERCHANTID);
31
+ }
32
+
33
+ public function getAllowSpecificPayments()
34
+ {
35
+ return Mage::getStoreConfig(self::XML_PATH_ALLOWSPECIFIC);
36
+ }
37
+
38
+ public function getSpecificPayments()
39
+ {
40
+ return explode(',', Mage::getStoreConfig(self::XML_PATH_SPECIFICPAYMENTS));
41
+ }
42
+
43
+ public function getApiKey()
44
+ {
45
+ return (string) Mage::getStoreConfig(self::XML_PATH_APIKEY);
46
+ }
47
+
48
+ public function isSandbox()
49
+ {
50
+ return Mage::getStoreConfigFlag(self::XML_PATH_SANDBOX);
51
+ }
52
+
53
+ public function isDebug()
54
+ {
55
+ return Mage::getStoreConfigFlag(self::XML_PATH_DEBUG);
56
+ }
57
+
58
+ public function getCost()
59
+ {
60
+ return (float) Mage::getStoreConfig(self::XML_PATH_COST);
61
+ }
62
+
63
+ public function getTosUrl()
64
+ {
65
+ return Mage::getStoreConfig(self::XML_PATH_TOSURL);
66
+ }
67
+
68
+ public function getGMapsKey()
69
+ {
70
+ return Mage::getStoreConfig(self::XML_PATH_GMAPSKEY);
71
+ }
72
+
73
+ public function getEndpointUrl($method)
74
+ {
75
+ $apiVersion = $method !== 'validMerchant'
76
+ ? self::API_VERSION
77
+ : self::API_VERSION_HACK
78
+ ;
79
+
80
+ return strtr( ! $this->isSandbox() ? self::ENDPOINT_PRODUCTION : self::ENDPOINT_SANDBOX, array(
81
+ ':api_version' => $apiVersion,
82
+ ':api_method' => $method,
83
+ ));
84
+ }
85
+
86
+ }
app/code/local/IndaBox/StorePickup/Helper/Data.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+
6
+ public function setUseMethod($flag)
7
+ {
8
+ $data = array(
9
+ 'use_storepickup' => $flag,
10
+ );
11
+ $session = Mage::getSingleton('checkout/session');
12
+ $session->setData('indabox_storepickup', $data);
13
+ }
14
+
15
+ public function getUseMethod()
16
+ {
17
+ $session = Mage::getSingleton('checkout/session');
18
+ $data = $session->getData('indabox_storepickup');
19
+ return is_array($data) && $data['use_storepickup'];
20
+ }
21
+
22
+ public function setPointId($pointId)
23
+ {
24
+ $this->setUseMethod(true);
25
+ $session = Mage::getSingleton('checkout/session');
26
+ $data = $session->getData('indabox_storepickup');
27
+ $data['point_id'] = $pointId;
28
+ $session->setData('indabox_storepickup', $data);
29
+ }
30
+
31
+ public function getPointId()
32
+ {
33
+ $session = Mage::getSingleton('checkout/session');
34
+ $data = $session->getData('indabox_storepickup');
35
+ if ( ! is_array($data) || ! isset($data['point_id']))
36
+ return 0;
37
+ else
38
+ return $data['point_id'];
39
+ }
40
+
41
+ public function getChangeMethodUrl()
42
+ {
43
+ return $this->_getUrl('ibstorepickup/index/changemethod', array('_secure' => true));
44
+ }
45
+
46
+ public function getSearchUrl()
47
+ {
48
+ return $this->_getUrl('ibstorepickup/index/search', array('_secure' => true));
49
+ }
50
+
51
+ public function getLocationUrl()
52
+ {
53
+ return $this->_getUrl('ibstorepickup/index/location', array('_secure' => true));
54
+ }
55
+
56
+ public function getMediaUrl()
57
+ {
58
+ return Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA, true) . 'indabox/';
59
+ }
60
+
61
+ public function getCustomerAddress()
62
+ {
63
+ $cSession = Mage::getSingleton('customer/session');
64
+
65
+ $attribute = Mage::getModel("eav/entity_attribute")->load("customer_shipping_address_id","attribute_code");
66
+
67
+ if($cSession->isLoggedIn() && $attribute->getId())
68
+ {
69
+ $address = Mage::helper('accountfield')
70
+ ->getShippingAddressByCustomerId($cSession->getCustomer()->getId());
71
+ if($address)
72
+ return $address;
73
+ }
74
+
75
+ $cart = Mage::getSingleton('checkout/cart');
76
+ return $cart->getQuote()->getShippingAddress();
77
+ }
78
+ }
app/code/local/IndaBox/StorePickup/Model/Api.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Api {
4
+
5
+ protected function _hideApiKey($debugData, $apiKey)
6
+ {
7
+ if (is_array($debugData) && strlen($apiKey)) {
8
+ foreach ($debugData as $key => &$value) {
9
+ if (is_string($value))
10
+ $value = str_replace($apiKey, '******', $value);
11
+ else
12
+ $value = $this->_hideApiKey($value, $apiKey);
13
+ }
14
+ }
15
+ return $debugData;
16
+ }
17
+
18
+ protected function _debug($debugData)
19
+ {
20
+ if (Mage::helper('ibstorepickup/config')->isDebug()) {
21
+ $debugData = $this->_hideApiKey($debugData, Mage::helper('ibstorepickup/config')->getApiKey());
22
+ Mage::getModel('core/log_adapter', 'indabox_storepickup.log')
23
+ ->log($debugData);
24
+ }
25
+ }
26
+
27
+ protected function _getToken($merchantId, $apiKey, $body)
28
+ {
29
+ return hash_hmac("sha256", $body, $merchantId . $apiKey);
30
+ }
31
+
32
+ public function call($method, $params)
33
+ {
34
+ $config = Mage::helper('ibstorepickup/config');
35
+ $merchantId = $config->getMerchantId();
36
+ if (empty($merchantId))
37
+ throw new IndaBox_StorePickup_Exception(Mage::helper('ibstorepickup')->__('Please specify Merchant ID'));
38
+ $apiKey = $config->getApiKey();
39
+ if (empty($apiKey))
40
+ throw new IndaBox_StorePickup_Exception(Mage::helper('ibstorepickup')->__('Please specify API Key'));
41
+
42
+ $body = json_encode($params);
43
+ $post = array(
44
+ 'id_merchant' => $merchantId,
45
+ 'msg' => $body,
46
+ 'token' => $this->_getToken($merchantId, $apiKey, $body),
47
+ );
48
+
49
+ $this->_debug($post);
50
+
51
+ $client = new Zend_Http_Client($config->getEndpointUrl($method));
52
+ $client->setParameterPost($post);
53
+ $client->setHeaders(array('Accept-encoding: identity'));
54
+ $client->setConfig(array('strictredirects' => true));
55
+
56
+ try {
57
+ $response = $client->request(Zend_Http_Client::POST);
58
+ } catch (Zend_Http_Client_Exception $e) {
59
+ Mage::logException($e);
60
+ throw new IndaBox_StorePickup_Exception(Mage::helper('ibstorepickup')->__('Could not communicate with server'));
61
+ }
62
+ $body = $response->getBody();
63
+
64
+ $json = json_decode($body, true);
65
+ if ($json === null)
66
+ {
67
+ $this->_debug($body);
68
+ throw new IndaBox_StorePickup_Exception(Mage::helper('ibstorepickup')->__('Invalid server reply'));
69
+ }
70
+
71
+ $this->_debug($json);
72
+
73
+ return $json;
74
+ }
75
+
76
+ public function validateMerchant()
77
+ {
78
+ $apiKey = Mage::helper('ibstorepickup/config')->getApiKey();
79
+ $result = $this->call('validMerchant', array(
80
+ 'api_key' => $apiKey,
81
+ ));
82
+
83
+ return $result === 'OK';
84
+ }
85
+
86
+ public function getPoints($address, $radius)
87
+ {
88
+ $points = $this->call('getPoints', array(
89
+ 'search' => $address,
90
+ 'radius' => $radius,
91
+ ));
92
+
93
+ if ( ! is_array($points))
94
+ return array();
95
+
96
+ $result = array();
97
+ foreach ($points as $data)
98
+ {
99
+ $point = Mage::getModel('ibstorepickup/point');
100
+ $point->setData($data);
101
+ $result[] = $point;
102
+ }
103
+
104
+ return $result;
105
+ }
106
+
107
+ public function submitOrder(Mage_Sales_Model_Order $order, IndaBox_StorePickup_Model_Order_Point $orderPoint)
108
+ {
109
+ $billingAddress = $order->getBillingAddress();
110
+ $result = $this->call('submitOrder', array(
111
+ 'ref' => $order->getId(),
112
+ 'ibp' => array(
113
+ 'pointid' => $orderPoint->getPointId(),
114
+ 'name' => $orderPoint->getPointName(),
115
+ ),
116
+ 'rcv' => array(
117
+ 'firstName' => $billingAddress->getFirstname(),
118
+ 'surname' => $billingAddress->getLastname(),
119
+ 'street' => $billingAddress->getStreetFull(),
120
+ 'postalCode' => $billingAddress->getPostcode(),
121
+ 'city' => $billingAddress->getCity(),
122
+ 'country' => $billingAddress->getCountryId(),
123
+ 'email' => $order->getCustomerEmail(),
124
+ 'mobile' => str_replace('+', '', preg_replace('#\+\.\.#', '', $billingAddress->getTelephone())),
125
+ ),
126
+ 'pcl' => array(
127
+ 'weight' => $order->getWeight(),
128
+ 'orderNumber' => $order->getIncrementId(),
129
+ 'orderDate' => $order->getCreatedAt(),
130
+ ),
131
+ ));
132
+
133
+ if ( ! is_array($result) || ! isset($result['codice']))
134
+ throw new IndaBox_StorePickup_Exception(Mage::helper('ibstorepickup')->__('Could not submit order'));
135
+
136
+ return $result['codice'];
137
+ }
138
+
139
+ public function trackShipment($trackNumber)
140
+ {
141
+ $rows = $this->call('getShipmentStatusbyTid', array(
142
+ 'transaction_ids' => is_array($trackNumber) ? $trackNumber : array($trackNumber),
143
+ ));
144
+
145
+ $result = array();
146
+ foreach ($rows as $row)
147
+ $result[$row['ref']] = $row;
148
+
149
+ return $result;
150
+ }
151
+
152
+ }
app/code/local/IndaBox/StorePickup/Model/Carrier/Storepickup.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Carrier_Storepickup
4
+ extends Mage_Shipping_Model_Carrier_Abstract
5
+ implements Mage_Shipping_Model_Carrier_Interface
6
+ {
7
+
8
+ protected $_code = 'ibstorepickup';
9
+
10
+
11
+ public function getCode()
12
+ {
13
+ return $this->_code;
14
+ }
15
+
16
+ public function collectRates(Mage_Shipping_Model_Rate_Request $request)
17
+ {
18
+ if ( ! $this->getConfigFlag('active'))
19
+ return false;
20
+
21
+ if ( ! Mage::helper('ibstorepickup/config')->getTosAccepted())
22
+ return false;
23
+
24
+ $items = $request->getAllItems();
25
+ if ( ! count($items))
26
+ return false;
27
+
28
+ if ($request->getDestCountryId() !== 'IT')
29
+ return false;
30
+
31
+ if ($request->getPackageWeight() > $this->getConfigData('maximum_weight'))
32
+ return false;
33
+
34
+ if ($request->getBaseSubtotalInclTax() > $this->getConfigData('maximum_subtotal'))
35
+ return false;
36
+
37
+ $api = Mage::getSingleton('ibstorepickup/api');
38
+ try {
39
+ $result = $api->validateMerchant();
40
+ } catch (IndaBox_StorePickup_Exception $e) {
41
+ return false;
42
+ }
43
+ if ( ! $result)
44
+ return false;
45
+
46
+ $cost = Mage::helper('ibstorepickup/config')->getCost();
47
+
48
+ $result = Mage::getModel('shipping/rate_result');
49
+ $method = Mage::getModel('shipping/rate_result_method');
50
+ $method->setCarrier('ibstorepickup');
51
+ $method->setCarrierTitle($this->getConfigData('title'));
52
+ $method->setMethod('ibstorepickup');
53
+ $method->setMethodTitle('');
54
+ $method->setPrice($cost);
55
+ $method->setCost($cost);
56
+ $result->append($method);
57
+
58
+ return $result;
59
+ }
60
+
61
+ public function getAllowedMethods()
62
+ {
63
+ return array(
64
+ 'ibstorepickup' => 'ibstorepickup',
65
+ );
66
+ }
67
+
68
+ public function getTrackingInfo($tracking)
69
+ {
70
+ $info = array();
71
+
72
+ $result = $this->getTracking($tracking);
73
+
74
+ if($result instanceof Mage_Shipping_Model_Tracking_Result){
75
+ if ($trackings = $result->getAllTrackings()) {
76
+ return $trackings[0];
77
+ }
78
+ }
79
+ elseif (is_string($result) && !empty($result)) {
80
+ return $result;
81
+ }
82
+
83
+ return false;
84
+ }
85
+
86
+ public function getTracking($trackings)
87
+ {
88
+ if ( ! is_array($trackings))
89
+ $trackings = array($trackings);
90
+
91
+ $errorTitle = Mage::helper('ibstorepickup')->__('Unable to retrieve tracking');
92
+
93
+ $raw = Mage::getSingleton('ibstorepickup/api')->trackShipment($trackings);
94
+
95
+ $success = array();
96
+ $error = array();
97
+ foreach ($raw as $idx => $row)
98
+ if ($row['esiste'])
99
+ {
100
+ list($date, $time) = explode(' ', $row['data']);
101
+ $success[$idx] = array(
102
+ 'deliverydate' => $date,
103
+ 'deliverytime' => $time,
104
+ 'status' => $row['stato'],
105
+ );
106
+ }
107
+ else
108
+ $error[$idx] = true;
109
+
110
+ $result = Mage::getModel('shipping/tracking_result');
111
+ if ($success || $error) {
112
+ foreach ($error as $t => $r) {
113
+ $error = Mage::getModel('shipping/tracking_result_error');
114
+ $error->setCarrier('ibstorepickup');
115
+ $error->setCarrierTitle($this->getConfigData('title'));
116
+ $error->setTracking($t);
117
+ $error->setErrorMessage($errorTitle);
118
+ $result->append($error);
119
+ }
120
+
121
+ foreach ($success as $t => $data) {
122
+ $tracking = Mage::getModel('shipping/tracking_result_status');
123
+ $tracking->setCarrier('ibstorepickup');
124
+ $tracking->setCarrierTitle($this->getConfigData('title'));
125
+ $tracking->setTracking($t);
126
+ $tracking->addData($data);
127
+
128
+ $result->append($tracking);
129
+ }
130
+ } else {
131
+ foreach ($trackings as $t) {
132
+ $error = Mage::getModel('shipping/tracking_result_error');
133
+ $error->setCarrier('ibstorepickup');
134
+ $error->setCarrierTitle($this->getConfigData('title'));
135
+ $error->setTracking($t);
136
+ $error->setErrorMessage($errorTitle);
137
+ $result->append($error);
138
+
139
+ }
140
+ }
141
+
142
+ return $result;
143
+ }
144
+
145
+ }
app/code/local/IndaBox/StorePickup/Model/GoogleMaps.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_GoogleMaps
4
+ {
5
+ const GEOCODER_URL = 'http://maps.google.com/maps/api/geocode/json?';
6
+ const STATUS_OK = 'ok';
7
+
8
+ const CACHE_TAG = 'indabox_gmaps';
9
+ const CACHE_KEY = 'indabox_gmaps_location_%s';
10
+ const CACHE_LIFETIME = 604800; // 1 week
11
+
12
+ protected function getLocationFromGoogle($address)
13
+ {
14
+ $params = array(
15
+ 'sensor' => 'false',
16
+ 'address' => $address,
17
+ );
18
+ if ($apiKey = Mage::helper('ibstorepickup/config')->getGMapsKey())
19
+ $params['key'] = $apiKey;
20
+
21
+ $url = self::GEOCODER_URL . http_build_query($params);
22
+
23
+ $client = new Zend_Http_Client($url);
24
+ $client->setHeaders(array('Accept-encoding: identity'));
25
+ $client->setConfig(array('strictredirects' => true));
26
+
27
+ try {
28
+ $response = $client->request(Zend_Http_Client::POST);
29
+ } catch (Zend_Http_Client_Exception $e) {
30
+ Mage::logException($e);
31
+ return null;
32
+ }
33
+
34
+ $body = $response->getBody();
35
+ $json = json_decode($body, true);
36
+ if ($json === null)
37
+ return null;
38
+
39
+ if ( ! isset($json['status']) || strtolower($json['status']) !== self::STATUS_OK)
40
+ return null;
41
+
42
+ if ( ! isset($json['results']) || ! is_array($json['results']) || ! count($json['results']))
43
+ return null;
44
+
45
+ $result = reset($json['results']);
46
+ return array(
47
+ $result['geometry']['location']['lat'],
48
+ $result['geometry']['location']['lng'],
49
+ );
50
+ }
51
+
52
+ public function getLocation($address)
53
+ {
54
+ $cacheKey = sprintf(self::CACHE_KEY, md5($address));
55
+ $cache = Mage::app()->getCache();
56
+ $value = unserialize($cache->load($cacheKey));
57
+ if ( ! is_array($value) || $value['address'] != $address)
58
+ {
59
+ $value = array(
60
+ 'address' => $address,
61
+ 'location' => $this->getLocationFromGoogle($address),
62
+ );
63
+
64
+ $cache->save(serialize($value), $cacheKey, array(self::CACHE_TAG), self::CACHE_LIFETIME);
65
+ }
66
+ return $value['location'];
67
+ }
68
+
69
+ }
app/code/local/IndaBox/StorePickup/Model/Observer.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Observer
4
+ {
5
+
6
+ public function onSaveShippingMethod($event)
7
+ {
8
+ $quote = $event->getQuote();
9
+ if ($quote->getShippingAddress()->getShippingMethod() !== 'ibstorepickup_ibstorepickup')
10
+ {
11
+ Mage::helper('ibstorepickup')->setUseMethod(false);
12
+ return;
13
+ }
14
+ $request = $event->getRequest();
15
+ $pointId = $request->getPost('indabox_point_id');
16
+ if ( ! $pointId)
17
+ {
18
+ Mage::helper('ibstorepickup')->setUseMethod(false);
19
+ return;
20
+ }
21
+
22
+ Mage::helper('ibstorepickup')->setPointId($pointId);
23
+ $point = Mage::getSingleton('ibstorepickup/points')->getPoint($pointId);
24
+
25
+ if ($point->getLatitude())
26
+ {
27
+ $address = $quote->getShippingAddress();
28
+ $address->addData($point->getAddressData());
29
+
30
+ $address->implodeStreetAddress();
31
+ $address->setCollectShippingRates(true);
32
+
33
+ $quote->collectTotals()->save();
34
+ }
35
+ }
36
+
37
+ public function onSaveOrderAfter($event)
38
+ {
39
+ $order = $event->getOrder();
40
+ $shippingMethod = $order->getShippingMethod();
41
+ if ($shippingMethod !== 'ibstorepickup_ibstorepickup')
42
+ return;
43
+
44
+ $pointId = Mage::helper('ibstorepickup')->getPointId();
45
+ if ( ! $pointId)
46
+ return;
47
+
48
+ $point = Mage::getSingleton('ibstorepickup/points')->getPoint($pointId);
49
+ $orderPoint = Mage::getModel('ibstorepickup/order_point');
50
+ $orderPoint->createFor($order, $point);
51
+
52
+ Mage::helper('ibstorepickup')->setUseMethod(false);
53
+ }
54
+
55
+ public function onOrderInvoicePay($event)
56
+ {
57
+ $invoice = $event->getInvoice();
58
+ $order = $invoice->getOrder();
59
+
60
+ $orderId = $order->getId();
61
+ $orderPoint = Mage::getModel('ibstorepickup/order_point')->load($orderId, 'order_id');
62
+ if ( ! $orderPoint->getId() || $orderPoint->getIsNotified())
63
+ return;
64
+
65
+ try {
66
+ $trackingNumber = Mage::getSingleton('ibstorepickup/api')->submitOrder($order, $orderPoint);
67
+ } catch (IndaBox_StorePickup_Exception $e) {
68
+ Mage::logException($e);
69
+ return;
70
+ }
71
+
72
+ $orderPoint->setIsNotified(true);
73
+ $orderPoint->save();
74
+
75
+ if ( ! $order->canShip())
76
+ return;
77
+
78
+ try {
79
+ $shipment = $order->prepareShipment();
80
+ if ($shipment)
81
+ {
82
+ $shipment->register();
83
+ $shipment->getOrder()->setIsInProcess(true);
84
+
85
+ $track = Mage::getModel('sales/order_shipment_track')
86
+ ->setShipment($shipment)
87
+ ->setData('title', 'IndaBox')
88
+ ->setData('number', $trackingNumber)
89
+ ->setData('carrier_code', 'ibstorepickup')
90
+ ->setData('order_id', $shipment->getData('order_id'))
91
+ ;
92
+
93
+ $transactionSave = Mage::getModel('core/resource_transaction')
94
+ ->addObject($shipment)
95
+ ->addObject($shipment->getOrder())
96
+ ->addObject($track)
97
+ ->save()
98
+ ;
99
+ }
100
+ } catch (Mage_Core_Exception $e) {
101
+ Mage::logException($e);
102
+ }
103
+ }
104
+
105
+ }
app/code/local/IndaBox/StorePickup/Model/Order/Point.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Order_Point extends Mage_Core_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('ibstorepickup/order_point');
9
+ }
10
+
11
+ public function createFor(Mage_Sales_Model_Order $order, IndaBox_StorePickup_Model_Point $point)
12
+ {
13
+ $this->setData('order_id', $order->getId());
14
+ $this->setData('point_id', $point->getId());
15
+ $this->setData('point_name', $point->getName());
16
+ $this->setData('point_address', $point->getFormattedAddress());
17
+ $this->setData('point_data', serialize($point->getData()));
18
+ $this->setData('is_notified', false);
19
+ return $this->save();
20
+ }
21
+
22
+ public function getPoint()
23
+ {
24
+ return Mage::getModel('ibstorepickup/point')->setData(unserialize($this->getPointData()));
25
+ }
26
+
27
+ }
app/code/local/IndaBox/StorePickup/Model/Point.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Point extends Varien_Object {
4
+
5
+ public function getId()
6
+ {
7
+ return $this->_getData('pointid');
8
+ }
9
+
10
+ public function getName()
11
+ {
12
+ return $this->_getData('name');
13
+ }
14
+
15
+ public function getStreet()
16
+ {
17
+ $parts = array();
18
+ foreach (array('toponimo', 'indirizzo', 'civico') as $key)
19
+ {
20
+ $value = trim($this->_getData($key));
21
+ if ( ! empty($value))
22
+ {
23
+ if ($key === 'civico')
24
+ $value = ', ' . $value;
25
+ else
26
+ $value = ' ' . $value;
27
+ $parts[] = $value;
28
+ }
29
+ }
30
+ return trim(implode('', $parts));
31
+ }
32
+
33
+ public function getCity()
34
+ {
35
+ return $this->_getData('comune');
36
+ }
37
+
38
+ public function getPostcode()
39
+ {
40
+ return $this->_getData('cap');
41
+ }
42
+
43
+ public function getRegion()
44
+ {
45
+ return $this->_getData('provincia');
46
+ }
47
+
48
+ public function getRegionId()
49
+ {
50
+ $regionModel = Mage::getModel('directory/region')->loadByCode($this->getRegion(), $this->getCountryId());
51
+ return $regionModel->getId();
52
+ }
53
+
54
+ public function getCountryId()
55
+ {
56
+ // hardcoded
57
+ return 'IT';
58
+ }
59
+
60
+ public function getTelephone()
61
+ {
62
+ return $this->_getData('tel');
63
+ }
64
+
65
+ public function getLatitude()
66
+ {
67
+ return $this->_getData('lat');
68
+ }
69
+
70
+ public function getLongitude()
71
+ {
72
+ return $this->_getData('lng');
73
+ }
74
+
75
+ public function getDistance()
76
+ {
77
+ return $this->_getData('distance');
78
+ }
79
+
80
+ public function getIndex()
81
+ {
82
+ return $this->_getData('indice');
83
+ }
84
+
85
+ public function getHours()
86
+ {
87
+ return $this->_getData('orari');
88
+ }
89
+
90
+ public function getUri()
91
+ {
92
+ return $this->_getData('uri');
93
+ }
94
+
95
+ public function getAddressData()
96
+ {
97
+ return array(
98
+ 'firstname' => Mage::helper('ibstorepickup')->__('IndaBox Point'),
99
+ 'lastname' => $this->getName(),
100
+ 'street' => $this->getStreet(),
101
+ 'city' => $this->getCity(),
102
+ 'region' => $this->getRegion(),
103
+ 'region_id' => $this->getRegionId(),
104
+ 'postcode' => $this->getPostcode(),
105
+ 'country_id' => $this->getCountryId(),
106
+ 'telephone' => $this->getTelephone(),
107
+ );
108
+ }
109
+
110
+ public function getFormattedAddress()
111
+ {
112
+ $address = Mage::getModel('customer/address');
113
+ $address->setData($this->getAddressData());
114
+
115
+ return $address->format('oneline');
116
+ }
117
+
118
+ public function toArray(array $arrAttributes = array())
119
+ {
120
+ $result = array(
121
+ 'id' => $this->getId(),
122
+ 'name' => $this->getName(),
123
+ );
124
+ $result = array_merge($result, $this->getAddressData());
125
+ $result = array_merge($result, array(
126
+ 'latitude' => $this->getLatitude(),
127
+ 'longitude' => $this->getLongitude(),
128
+ 'distance' => $this->getDistance(),
129
+ 'url' => $this->getUri(),
130
+ ));
131
+
132
+ return $result;
133
+ }
134
+
135
+ }
app/code/local/IndaBox/StorePickup/Model/Points.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Points {
4
+
5
+ const CACHE_TAG = 'indabox_points';
6
+ const CACHE_KEY = 'indabox_points_%s_%s';
7
+ const CACHE_LIFETIME = 3600; // 1 hour
8
+
9
+ const POINT_CACHE_KEY = 'indabox_point_%d';
10
+ const POINT_CACHE_LIFETIME = 604800; // 1 week
11
+
12
+ protected function storePoint($point)
13
+ {
14
+ $cacheKey = sprintf(self::POINT_CACHE_KEY, $point->getId());
15
+ $cache = Mage::app()->getCache();
16
+ $cache->save(serialize($point), $cacheKey, array(self::CACHE_TAG), self::POINT_CACHE_LIFETIME);
17
+ }
18
+
19
+ public function getPoint($pointId)
20
+ {
21
+ $cacheKey = sprintf(self::POINT_CACHE_KEY, $pointId);
22
+ $cache = Mage::app()->getCache();
23
+ $value = unserialize($cache->load($cacheKey));
24
+ if ( ! $value instanceof IndaBox_StorePickup_Model_Point)
25
+ {
26
+ $value = Mage::getModel('ibstorepickup/point');
27
+ $value->setData(array(
28
+ 'pointid' => $pointId,
29
+ 'name' => 'Point #' . $pointId,
30
+ ));
31
+ }
32
+
33
+ return $value;
34
+ }
35
+
36
+ public function getPoints($address, $radius)
37
+ {
38
+ $cacheKey = sprintf(self::CACHE_KEY, md5($address), $radius);
39
+ $cache = Mage::app()->getCache();
40
+ $value = unserialize($cache->load($cacheKey));
41
+ if ( ! is_array($value) || $value['address'] != $address || $value['radius'] != $radius)
42
+ {
43
+ $points = Mage::getSingleton('ibstorepickup/api')->getPoints($address, $radius);
44
+ foreach ($points as $point)
45
+ $this->storePoint($point);
46
+
47
+ $value = array(
48
+ 'address' => $address,
49
+ 'radius' => $radius,
50
+ 'location' => $points,
51
+ );
52
+
53
+ $cache->save(serialize($value), $cacheKey, array(self::CACHE_TAG), self::CACHE_LIFETIME);
54
+ }
55
+ return $value['location'];
56
+ }
57
+
58
+ }
app/code/local/IndaBox/StorePickup/Model/Resource/Order/Point.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Resource_Order_Point extends Mage_Core_Model_Resource_Db_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('ibstorepickup/order_point', 'order_point_id');
8
+ }
9
+ }
app/code/local/IndaBox/StorePickup/Model/Resource/Order/Point/Collection.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Resource_Order_Point_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('ibstorepickup/order_point');
9
+ }
10
+ }
app/code/local/IndaBox/StorePickup/Model/Source/Payment.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Source_Payment
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ $collection = Mage::getModel('payment/config')->getActiveMethods();
8
+
9
+ if ( ! count($collection))
10
+ return;
11
+
12
+ $options = array();
13
+ foreach ($collection as $item)
14
+ {
15
+ $title = $item->getTitle() ? $item->getTitle() : $item->getId();
16
+ $options[] = array('value' => $item->getId(), 'label' => $title);
17
+ }
18
+
19
+ return $options;
20
+ }
21
+ }
app/code/local/IndaBox/StorePickup/Model/Source/Selectorpayment.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_Model_Source_Selectorpayment
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => 0, 'label' => Mage::helper('ibstorepickup')->__('All Allowed Payments')),
9
+ array('value' => 1, 'label' => Mage::helper('ibstorepickup')->__('Specific Payments')),
10
+ );
11
+ }
12
+ }
app/code/local/IndaBox/StorePickup/controllers/IndexController.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class IndaBox_StorePickup_IndexController extends Mage_Core_Controller_Front_Action
4
+ {
5
+
6
+ public function changemethodAction()
7
+ {
8
+ $flag = (bool) $this->getRequest()->getParam('flag');
9
+ Mage::helper('ibstorepickup')->setUseMethod($flag);
10
+ }
11
+
12
+ public function searchAction()
13
+ {
14
+ $this->getResponse()->setHeader('Content-type', 'application/json');
15
+
16
+ $response = new Varien_Object();
17
+ $response->setError(false);
18
+
19
+ $address = (string) $this->getRequest()->getParam('address', '');
20
+ $radius = (string) $this->getRequest()->getParam('radius', '');
21
+
22
+ if (empty($address) || $radius <= 0)
23
+ {
24
+ $response->setError(true);
25
+ $response->setMessage($this->__('Invalid search parameters.'));
26
+
27
+ $this->getResponse()->setBody($response->toJson());
28
+ return;
29
+ }
30
+
31
+ try {
32
+ $points = Mage::getSingleton('ibstorepickup/points')->getPoints($address, $radius);
33
+ $list = array();
34
+ foreach ($points as $point)
35
+ $list[] = $point->toArray();
36
+ $response->setPoints($list);
37
+ } catch (IndaBox_StorePickup_Exception $e) {
38
+ $response->setError(true);
39
+ $response->setMessage($this->__('Error while communicating with IndaBox. Please retry later.'));
40
+ $this->getResponse()->setBody($response->toJson());
41
+ }
42
+
43
+ $this->getResponse()->setBody($response->toJson());
44
+ }
45
+
46
+ public function locationAction()
47
+ {
48
+ $this->getResponse()->setHeader('Content-type', 'application/json');
49
+
50
+ $response = new Varien_Object();
51
+ $response->setError(false);
52
+
53
+ $address = (string) $this->getRequest()->getParam('address', '');
54
+
55
+ if (empty($address))
56
+ {
57
+ $response->setError(true);
58
+ $response->setMessage($this->__('Invalid search parameters.'));
59
+
60
+ $this->getResponse()->setBody($response->toJson());
61
+ return;
62
+ }
63
+
64
+ $location = Mage::getSingleton('ibstorepickup/googleMaps')->getLocation($address);
65
+ if ($location !== null)
66
+ {
67
+ $response->setLatitude($location[0]);
68
+ $response->setLongitude($location[1]);
69
+ }
70
+ else
71
+ {
72
+ $response->setError(true);
73
+ $response->setMessage($this->__('Location does not found'));
74
+ }
75
+
76
+ $this->getResponse()->setBody($response->toJson());
77
+ }
78
+
79
+ }
app/code/local/IndaBox/StorePickup/etc/config.xml ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+
4
+ <modules>
5
+ <IndaBox_StorePickup>
6
+ <version>0.1.0</version>
7
+ </IndaBox_StorePickup>
8
+ </modules>
9
+
10
+ <global>
11
+
12
+ <models>
13
+ <ibstorepickup>
14
+ <class>IndaBox_StorePickup_Model</class>
15
+ <resourceModel>ibstorepickup_resource</resourceModel>
16
+ </ibstorepickup>
17
+ <ibstorepickup_resource>
18
+ <class>IndaBox_StorePickup_Model_Resource</class>
19
+ <entities>
20
+ <order_point>
21
+ <table>ibsp_order_point</table>
22
+ </order_point>
23
+ </entities>
24
+ </ibstorepickup_resource>
25
+ </models>
26
+
27
+ <blocks>
28
+ <ibstorepickup>
29
+ <class>IndaBox_StorePickup_Block</class>
30
+ </ibstorepickup>
31
+ <checkout>
32
+ <rewrite>
33
+ <onepage_payment_methods>IndaBox_StorePickup_Block_Checkout_Onepage_Payment_Methods</onepage_payment_methods>
34
+ <onepage_shipping_method_available>IndaBox_StorePickup_Block_Checkout_Onepage_Shipping_Method_Available</onepage_shipping_method_available>
35
+ </rewrite>
36
+ </checkout>
37
+ </blocks>
38
+
39
+ <helpers>
40
+ <ibstorepickup>
41
+ <class>IndaBox_StorePickup_Helper</class>
42
+ </ibstorepickup>
43
+ </helpers>
44
+
45
+ <resources>
46
+ <ibstorepickup_setup>
47
+ <setup>
48
+ <module>IndaBox_StorePickup</module>
49
+ </setup>
50
+ <connection>
51
+ <use>core_setup</use>
52
+ </connection>
53
+ </ibstorepickup_setup>
54
+ <ibstorepickup_write>
55
+ <connection>
56
+ <use>core_write</use>
57
+ </connection>
58
+ </ibstorepickup_write>
59
+ <ibstorepickup_read>
60
+ <connection>
61
+ <use>core_read</use>
62
+ </connection>
63
+ </ibstorepickup_read>
64
+ </resources>
65
+
66
+ <events>
67
+ <sales_order_invoice_pay>
68
+ <observers>
69
+ <indabox_storepickup_observer>
70
+ <type>singleton</type>
71
+ <class>ibstorepickup/observer</class>
72
+ <method>onOrderInvoicePay</method>
73
+ </indabox_storepickup_observer>
74
+ </observers>
75
+ </sales_order_invoice_pay>
76
+ </events>
77
+
78
+ </global>
79
+
80
+ <frontend>
81
+
82
+ <translate>
83
+ <modules>
84
+ <IndaBox_StorePickup>
85
+ <files>
86
+ <default>IndaBox_StorePickup.csv</default>
87
+ </files>
88
+ </IndaBox_StorePickup>
89
+ </modules>
90
+ </translate>
91
+
92
+ <routers>
93
+ <ibstorepickup>
94
+ <use>standard</use>
95
+ <args>
96
+ <module>IndaBox_StorePickup</module>
97
+ <frontName>ibstorepickup</frontName>
98
+ </args>
99
+ </ibstorepickup>
100
+ </routers>
101
+
102
+ <layout>
103
+ <updates>
104
+ <ibstorepickup>
105
+ <file>ibstorepickup.xml</file>
106
+ </ibstorepickup>
107
+ </updates>
108
+ </layout>
109
+
110
+ <events>
111
+ <checkout_type_onepage_save_order_after>
112
+ <observers>
113
+ <indabox_storepickup_observer>
114
+ <type>singleton</type>
115
+ <class>ibstorepickup/observer</class>
116
+ <method>onSaveOrderAfter</method>
117
+ </indabox_storepickup_observer>
118
+ </observers>
119
+ </checkout_type_onepage_save_order_after>
120
+ <checkout_controller_onepage_save_shipping_method>
121
+ <observers>
122
+ <indabox_storepickup_observer>
123
+ <type>singleton</type>
124
+ <class>ibstorepickup/observer</class>
125
+ <method>onSaveShippingMethod</method>
126
+ </indabox_storepickup_observer>
127
+ </observers>
128
+ </checkout_controller_onepage_save_shipping_method>
129
+ </events>
130
+ </frontend>
131
+
132
+ <adminhtml>
133
+
134
+ <translate>
135
+ <modules>
136
+ <IndaBox_StorePickup>
137
+ <files>
138
+ <default>IndaBox_StorePickup.csv</default>
139
+ </files>
140
+ </IndaBox_StorePickup>
141
+ </modules>
142
+ </translate>
143
+
144
+
145
+ <layout>
146
+ <updates>
147
+ <ibstorepickup>
148
+ <file>ibstorepickup.xml</file>
149
+ </ibstorepickup>
150
+ </updates>
151
+ </layout>
152
+ </adminhtml>
153
+
154
+ <default>
155
+ <carriers>
156
+ <ibstorepickup>
157
+ <accept>0</accept>
158
+ <active>0</active>
159
+ <model>ibstorepickup/carrier_storepickup</model>
160
+ <title>IndaBox StorePickup</title>
161
+ <sandbox>1</sandbox>
162
+ <debug>1</debug>
163
+ <cost>0</cost>
164
+ <maximum_subtotal>500</maximum_subtotal>
165
+ <maximum_weight>15</maximum_weight>
166
+ <tos_url>http://www.indabox.it/condizioni-prestashop/</tos_url>
167
+ </ibstorepickup>
168
+ </carriers>
169
+ </default>
170
+
171
+ </config>
app/code/local/IndaBox/StorePickup/etc/jstranslator.xml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <jstranslator>
3
+ <indabox-agreement translate="message" module="ibstorepickup">
4
+ <message>You should accept IndaBox terms and conditions</message>
5
+ </indabox-agreement>
6
+ <indabox-select-point translate="message" module="ibstorepickup">
7
+ <message>You should select one of available pick-up points to continue</message>
8
+ </indabox-select-point>
9
+ <indabox-error translate="message" module="ibstorepickup">
10
+ <message>Error</message>
11
+ </indabox-error>
12
+ <indabox-use-point translate="message" module="ibstorepickup">
13
+ <message>Select this pick-up point</message>
14
+ </indabox-use-point>
15
+ <indabox-distance translate="message" module="ibstorepickup">
16
+ <message>Distance</message>
17
+ </indabox-distance>
18
+ <indabox-telephone translate="message" module="ibstorepickup">
19
+ <message>Telephone</message>
20
+ </indabox-telephone>
21
+ </jstranslator>
app/code/local/IndaBox/StorePickup/etc/system.xml ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <carriers>
5
+ <groups>
6
+ <ibstorepickup translate="label" module="ibstorepickup">
7
+ <label>IndaBox</label>
8
+ <frontend_type>text</frontend_type>
9
+ <sort_order>12</sort_order>
10
+ <show_in_default>1</show_in_default>
11
+ <show_in_website>1</show_in_website>
12
+ <show_in_store>1</show_in_store>
13
+ <fields>
14
+ <configuration_manual translate="label">
15
+ <label></label>
16
+ <frontend_model>ibstorepickup/adminhtml_configuration_manual</frontend_model>
17
+ <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
18
+ <sort_order>0</sort_order>
19
+ <show_in_default>1</show_in_default>
20
+ <show_in_website>1</show_in_website>
21
+ <show_in_store>0</show_in_store>
22
+ </configuration_manual>
23
+ <accept translate="label comment">
24
+ <label>Accept TOS</label>
25
+ <comment><![CDATA[I accept <a href="https://www.indabox.it/termini-eco/" target="_blank">IndaBox Terms of Service</a>]]></comment>
26
+ <frontend_type>select</frontend_type>
27
+ <source_model>adminhtml/system_config_source_yesno</source_model>
28
+ <sort_order>1</sort_order>
29
+ <show_in_default>1</show_in_default>
30
+ <show_in_website>1</show_in_website>
31
+ <show_in_store>0</show_in_store>
32
+ </accept>
33
+ <active translate="label">
34
+ <label>Enabled</label>
35
+ <frontend_type>select</frontend_type>
36
+ <source_model>adminhtml/system_config_source_yesno</source_model>
37
+ <sort_order>10</sort_order>
38
+ <show_in_default>1</show_in_default>
39
+ <show_in_website>1</show_in_website>
40
+ <show_in_store>0</show_in_store>
41
+ <depends><accept>1</accept></depends>
42
+ </active>
43
+ <title translate="label">
44
+ <label>Title</label>
45
+ <frontend_type>text</frontend_type>
46
+ <sort_order>11</sort_order>
47
+ <show_in_default>1</show_in_default>
48
+ <show_in_website>1</show_in_website>
49
+ <show_in_store>0</show_in_store>
50
+ <depends><accept>1</accept></depends>
51
+ </title>
52
+
53
+ <merchant_id translate="label">
54
+ <label>Merchant ID</label>
55
+ <frontend_type>text</frontend_type>
56
+ <sort_order>20</sort_order>
57
+ <show_in_default>1</show_in_default>
58
+ <show_in_website>1</show_in_website>
59
+ <show_in_store>0</show_in_store>
60
+ <depends><accept>1</accept></depends>
61
+ </merchant_id>
62
+ <api_key translate="label">
63
+ <label>API Key</label>
64
+ <frontend_type>textarea</frontend_type>
65
+ <sort_order>21</sort_order>
66
+ <show_in_default>1</show_in_default>
67
+ <show_in_website>1</show_in_website>
68
+ <show_in_store>0</show_in_store>
69
+ <depends><accept>1</accept></depends>
70
+ </api_key>
71
+ <sandbox translate="label">
72
+ <label>Sandbox</label>
73
+ <frontend_type>select</frontend_type>
74
+ <source_model>adminhtml/system_config_source_yesno</source_model>
75
+ <sort_order>22</sort_order>
76
+ <show_in_default>1</show_in_default>
77
+ <show_in_website>1</show_in_website>
78
+ <show_in_store>0</show_in_store>
79
+ <depends><accept>1</accept></depends>
80
+ </sandbox>
81
+ <debug translate="label">
82
+ <label>Debug</label>
83
+ <frontend_type>select</frontend_type>
84
+ <source_model>adminhtml/system_config_source_yesno</source_model>
85
+ <sort_order>23</sort_order>
86
+ <show_in_default>1</show_in_default>
87
+ <show_in_website>1</show_in_website>
88
+ <show_in_store>0</show_in_store>
89
+ <depends><accept>1</accept></depends>
90
+ </debug>
91
+
92
+ <cost translate="label">
93
+ <label>Cost</label>
94
+ <frontend_type>text</frontend_type>
95
+ <sort_order>30</sort_order>
96
+ <validate>validate-number</validate>
97
+ <show_in_default>1</show_in_default>
98
+ <show_in_website>1</show_in_website>
99
+ <show_in_store>0</show_in_store>
100
+ <depends><accept>1</accept></depends>
101
+ </cost>
102
+ <maximum_subtotal translate="label">
103
+ <label>Maximum Subtotal</label>
104
+ <frontend_type>text</frontend_type>
105
+ <sort_order>31</sort_order>
106
+ <validate>validate-number</validate>
107
+ <show_in_default>1</show_in_default>
108
+ <show_in_website>1</show_in_website>
109
+ <show_in_store>0</show_in_store>
110
+ <depends><accept>1</accept></depends>
111
+ </maximum_subtotal>
112
+ <maximum_weight translate="label">
113
+ <label>Maximum Weight</label>
114
+ <frontend_type>text</frontend_type>
115
+ <sort_order>32</sort_order>
116
+ <validate>validate-number</validate>
117
+ <show_in_default>1</show_in_default>
118
+ <show_in_website>1</show_in_website>
119
+ <show_in_store>0</show_in_store>
120
+ <depends><accept>1</accept></depends>
121
+ </maximum_weight>
122
+
123
+ <tos_url translate="label">
124
+ <label>Terms and Conditions Url</label>
125
+ <frontend_type>text</frontend_type>
126
+ <sort_order>40</sort_order>
127
+ <show_in_default>1</show_in_default>
128
+ <show_in_website>1</show_in_website>
129
+ <show_in_store>0</show_in_store>
130
+ <depends><accept>1</accept></depends>
131
+ </tos_url>
132
+ <gmaps_key translate="label comment">
133
+ <label>Google Maps API Key</label>
134
+ <frontend_type>text</frontend_type>
135
+ <sort_order>40</sort_order>
136
+ <show_in_default>1</show_in_default>
137
+ <show_in_website>1</show_in_website>
138
+ <show_in_store>0</show_in_store>
139
+ <comment>Optional, use for high load only</comment>
140
+ <depends><accept>1</accept></depends>
141
+ </gmaps_key>
142
+
143
+ <allowspecific_payment translate="label">
144
+ <label>Applicable payments</label>
145
+ <frontend_type>select</frontend_type>
146
+ <sort_order>100</sort_order>
147
+ <source_model>ibstorepickup/source_selectorpayment</source_model>
148
+ <show_in_default>1</show_in_default>
149
+ <show_in_website>1</show_in_website>
150
+ <show_in_store>0</show_in_store>
151
+ <depends><accept>1</accept></depends>
152
+ </allowspecific_payment>
153
+
154
+ <specificpayment translate="label">
155
+ <label>Specific payments</label>
156
+ <frontend_type>multiselect</frontend_type>
157
+ <sort_order>110</sort_order>
158
+ <source_model>ibstorepickup/source_payment</source_model>
159
+ <show_in_default>1</show_in_default>
160
+ <show_in_website>1</show_in_website>
161
+ <show_in_store>0</show_in_store>
162
+ <depends><allowspecific_payment>1</allowspecific_payment></depends>
163
+ </specificpayment>
164
+
165
+ </fields>
166
+ </ibstorepickup>
167
+ </groups>
168
+ </carriers>
169
+ </sections>
170
+ </config>
app/code/local/IndaBox/StorePickup/sql/ibstorepickup_setup/install-0.1.0.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $table = $installer->getConnection()
8
+ ->newTable($installer->getTable('ibstorepickup/order_point'))
9
+ ->addColumn('order_point_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
10
+ 'identity' => true,
11
+ 'unsigned' => true,
12
+ 'nullable' => false,
13
+ 'primary' => true,
14
+ ), 'Order Point ID'
15
+ )
16
+ ->addColumn('order_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
17
+ 'unsigned' => true,
18
+ 'nullable' => false,
19
+ 'default' => '0',
20
+ ), 'Order ID'
21
+ )
22
+ ->addColumn('point_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
23
+ 'unsigned' => true,
24
+ 'nullable' => false,
25
+ 'default' => '0',
26
+ ), 'Point ID'
27
+ )
28
+ ->addColumn('is_notified', Varien_Db_Ddl_Table::TYPE_BOOLEAN, null, array(
29
+ 'nullable' => false,
30
+ 'unsigned' => true,
31
+ 'default' => '0',
32
+ ), 'Is Notified Flag'
33
+ )
34
+ ->addColumn('point_name', Varien_Db_Ddl_Table::TYPE_VARCHAR, null, array(
35
+ 'nullable' => false,
36
+ 'default' => '',
37
+ 'length' => 255,
38
+ ), 'Point Name'
39
+ )
40
+ ->addColumn('point_address', Varien_Db_Ddl_Table::TYPE_TEXT, null, array(
41
+ 'nullable' => false,
42
+ 'default' => '',
43
+ ), 'Point Address'
44
+ )
45
+ ->addColumn('point_data', Varien_Db_Ddl_Table::TYPE_TEXT, null, array(
46
+ 'nullable' => false,
47
+ 'default' => '',
48
+ ), 'Serialized Point Data'
49
+ )
50
+ ->addIndex($installer->getIdxName('ibstorepickup/order_point', array('order_id')), array('order_id'))
51
+ ->addIndex($installer->getIdxName('ibstorepickup/order_point', array('point_id')), array('point_id'))
52
+ ->addForeignKey(
53
+ $installer->getFkName('ibstorepickup/order_point', 'order_id', 'sales/order', 'entity_id'),
54
+ 'order_id', $installer->getTable('sales/order'), 'entity_id',
55
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE
56
+ )
57
+ ->setComment('Indabox Storepickup Order to Point')
58
+ ;
59
+ $installer->getConnection()->createTable($table);
60
+
61
+ $installer->endSetup();
app/design/frontend/base/default/layout/ibstorepickup.xml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <default>
4
+ <reference name="head">
5
+ <action method="addJs"><script>indabox/storepickup.js</script></action>
6
+ <action method="addItem"><type>skin_css</type><name>indabox/css/storepickup.css</name></action>
7
+ </reference>
8
+ </default>
9
+
10
+ <!--checkout_cart_index>
11
+ <reference name="checkout.cart.shipping">
12
+ <action method="setTemplate"><template>storepickup/shippingtax.phtml</template></action>
13
+ <block type="storepickup/storepickup" name="storepickup" as="storepickup" >
14
+ <block type="storepickup/location" name="store_location" as="store_location" template="storepickup/store_location.phtml" />
15
+ <block type="storepickup/storepickup" name="list_store" as="list_store" template="storepickup/store.phtml" />
16
+ </block>
17
+ </reference>
18
+ </checkout_cart_index-->
19
+
20
+ <checkout_onepage_index>
21
+ <reference name="head">
22
+ <action method="addJs"><script>indabox/markerclusterer.js</script></action>
23
+ </reference>
24
+ <reference name="checkout.onepage.billing">
25
+ <action method="setTemplate"><template>ibstorepickup/checkout/onepage/billing.phtml</template></action>
26
+ </reference>
27
+ </checkout_onepage_index>
28
+
29
+ </layout>
app/design/frontend/base/default/template/ibstorepickup/checkout/onepage/billing.phtml ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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) 2013 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
25
+ */
26
+ ?>
27
+ <form id="co-billing-form" action="">
28
+ <fieldset>
29
+ <ul class="form-list">
30
+ <?php if ($this->customerHasAddresses()): ?>
31
+ <li class="wide">
32
+ <label for="billing-address-select"><?php echo $this->__('Select a billing address from your address book or enter a new address.') ?></label>
33
+ <div class="input-box">
34
+ <?php echo $this->getAddressesHtmlSelect('billing') ?>
35
+ </div>
36
+ </li>
37
+ <?php endif; ?>
38
+ <li id="billing-new-address-form"<?php if ($this->customerHasAddresses()): ?> style="display:none;"<?php endif; ?>>
39
+ <fieldset>
40
+ <input type="hidden" name="billing[address_id]" value="<?php echo $this->getAddress()->getId() ?>" id="billing:address_id" />
41
+ <ul>
42
+ <li class="fields"><?php echo $this->getLayout()->createBlock('customer/widget_name')->setObject($this->getAddress()->getFirstname() ? $this->getAddress() : $this->getQuote()->getCustomer())->setForceUseCustomerRequiredAttributes(!$this->isCustomerLoggedIn())->setFieldIdFormat('billing:%s')->setFieldNameFormat('billing[%s]')->toHtml() ?></li>
43
+ <li class="fields">
44
+ <div class="field">
45
+ <label for="billing:company"><?php echo $this->__('Company') ?></label>
46
+ <div class="input-box">
47
+ <input type="text" id="billing:company" name="billing[company]" value="<?php echo $this->escapeHtml($this->getAddress()->getCompany()) ?>" title="<?php echo $this->__('Company') ?>" class="input-text <?php echo $this->helper('customer/address')->getAttributeValidationClass('company') ?>" />
48
+ </div>
49
+ </div>
50
+ <?php if(!$this->isCustomerLoggedIn()): ?>
51
+ <div class="field">
52
+ <label for="billing:email" class="required"><em>*</em><?php echo $this->__('Email Address') ?></label>
53
+ <div class="input-box">
54
+ <input type="text" name="billing[email]" id="billing:email" value="<?php echo $this->escapeHtml($this->getAddress()->getEmail()) ?>" title="<?php echo $this->__('Email Address') ?>" class="input-text validate-email required-entry" />
55
+ </div>
56
+ </div>
57
+ <?php endif; ?>
58
+ </li>
59
+ <?php $_streetValidationClass = $this->helper('customer/address')->getAttributeValidationClass('street'); ?>
60
+ <li class="wide">
61
+ <label for="billing:street1" class="required"><em>*</em><?php echo $this->__('Address') ?></label>
62
+ <div class="input-box">
63
+ <input type="text" title="<?php echo $this->__('Street Address') ?>" name="billing[street][]" id="billing:street1" value="<?php echo $this->escapeHtml($this->getAddress()->getStreet(1)) ?>" class="input-text <?php echo $_streetValidationClass ?>" />
64
+ </div>
65
+ </li>
66
+ <?php $_streetValidationClass = trim(str_replace('required-entry', '', $_streetValidationClass)); ?>
67
+ <?php for ($_i = 2, $_n = $this->helper('customer/address')->getStreetLines(); $_i <= $_n; $_i++): ?>
68
+ <li class="wide">
69
+ <div class="input-box">
70
+ <input type="text" title="<?php echo $this->__('Street Address %s', $_i) ?>" name="billing[street][]" id="billing:street<?php echo $_i ?>" value="<?php echo $this->escapeHtml($this->getAddress()->getStreet($_i)) ?>" class="input-text <?php echo $_streetValidationClass ?>" />
71
+ </div>
72
+ </li>
73
+ <?php endfor; ?>
74
+ <?php if ($this->helper('customer/address')->isVatAttributeVisible()) : ?>
75
+ <li class="wide">
76
+ <label for="billing:vat_id"><?php echo $this->__('VAT Number') ?></label>
77
+ <div class="input-box">
78
+ <input type="text" id="billing:vat_id" name="billing[vat_id]" value="<?php echo $this->escapeHtml($this->getAddress()->getVatId()) ?>" title="<?php echo $this->__('VAT Number') ?>" class="input-text <?php echo $this->helper('customer/address')->getAttributeValidationClass('vat_id') ?>" />
79
+ </div>
80
+ </li>
81
+ <?php endif; ?>
82
+ <li class="fields">
83
+ <div class="field">
84
+ <label for="billing:city" class="required"><em>*</em><?php echo $this->__('City') ?></label>
85
+ <div class="input-box">
86
+ <input type="text" title="<?php echo $this->__('City') ?>" name="billing[city]" value="<?php echo $this->escapeHtml($this->getAddress()->getCity()) ?>" class="input-text <?php echo $this->helper('customer/address')->getAttributeValidationClass('city') ?>" id="billing:city" />
87
+ </div>
88
+ </div>
89
+ <div class="field">
90
+ <label for="billing:region_id" class="required"><em>*</em><?php echo $this->__('State/Province') ?></label>
91
+ <div class="input-box">
92
+ <select id="billing:region_id" name="billing[region_id]" title="<?php echo $this->__('State/Province') ?>" class="validate-select" style="display:none;">
93
+ <option value=""><?php echo $this->__('Please select region, state or province') ?></option>
94
+ </select>
95
+ <script type="text/javascript">
96
+ //<![CDATA[
97
+ $('billing:region_id').setAttribute('defaultValue', "<?php echo $this->getAddress()->getRegionId() ?>");
98
+ //]]>
99
+ </script>
100
+ <input type="text" id="billing:region" name="billing[region]" value="<?php echo $this->escapeHtml($this->getAddress()->getRegion()) ?>" title="<?php echo $this->__('State/Province') ?>" class="input-text <?php echo $this->helper('customer/address')->getAttributeValidationClass('region') ?>" style="display:none;" />
101
+ </div>
102
+ </div>
103
+ </li>
104
+ <li class="fields">
105
+ <div class="field">
106
+ <label for="billing:postcode" class="required"><em>*</em><?php echo $this->__('Zip/Postal Code') ?></label>
107
+ <div class="input-box">
108
+ <input type="text" title="<?php echo $this->__('Zip/Postal Code') ?>" name="billing[postcode]" id="billing:postcode" value="<?php echo $this->escapeHtml($this->getAddress()->getPostcode()) ?>" class="input-text validate-zip-international <?php echo $this->helper('customer/address')->getAttributeValidationClass('postcode') ?>" />
109
+ </div>
110
+ </div>
111
+ <div class="field">
112
+ <label for="billing:country_id" class="required"><em>*</em><?php echo $this->__('Country') ?></label>
113
+ <div class="input-box">
114
+ <?php echo $this->getCountryHtmlSelect('billing') ?>
115
+ </div>
116
+ </div>
117
+ </li>
118
+ <li class="fields">
119
+ <div class="field">
120
+ <label for="billing:telephone" class="required"><em>*</em><?php echo $this->__('Telephone') ?></label>
121
+ <div class="input-box">
122
+ <input type="text" name="billing[telephone]" value="<?php echo $this->escapeHtml($this->getAddress()->getTelephone()) ?>" title="<?php echo $this->__('Telephone') ?>" class="input-text <?php echo $this->helper('customer/address')->getAttributeValidationClass('telephone') ?>" id="billing:telephone" />
123
+ </div>
124
+ </div>
125
+ <div class="field">
126
+ <label for="billing:fax"><?php echo $this->__('Fax') ?></label>
127
+ <div class="input-box">
128
+ <input type="text" name="billing[fax]" value="<?php echo $this->escapeHtml($this->getAddress()->getFax()) ?>" title="<?php echo $this->__('Fax') ?>" class="input-text <?php echo $this->helper('customer/address')->getAttributeValidationClass('fax') ?>" id="billing:fax" />
129
+ </div>
130
+ </div>
131
+ </li>
132
+ <?php if(!$this->isCustomerLoggedIn()): ?>
133
+
134
+ <?php $_dob = $this->getLayout()->createBlock('customer/widget_dob') ?>
135
+ <?php $_gender = $this->getLayout()->createBlock('customer/widget_gender') ?>
136
+ <?php if ($_dob->isEnabled() || $_gender->isEnabled()): ?>
137
+ <li class="fields">
138
+ <?php if ($_dob->isEnabled()): ?>
139
+ <div class="field">
140
+ <?php echo $_dob->setDate($this->getQuote()->getCustomerDob())->setFieldIdFormat('billing:%s')->setFieldNameFormat('billing[%s]')->toHtml() ?>
141
+ </div>
142
+ <?php endif; ?>
143
+ <?php if ($_gender->isEnabled()): ?>
144
+ <div class="field">
145
+ <?php echo $_gender->setGender($this->getQuote()->getCustomerGender())->setFieldIdFormat('billing:%s')->setFieldNameFormat('billing[%s]')->toHtml() ?>
146
+ </div>
147
+ <?php endif ?>
148
+ </li>
149
+ <?php endif ?>
150
+
151
+ <?php $_taxvat = $this->getLayout()->createBlock('customer/widget_taxvat') ?>
152
+ <?php if ($_taxvat->isEnabled()): ?>
153
+ <li>
154
+ <?php echo $_taxvat->setTaxvat($this->getQuote()->getCustomerTaxvat())->setFieldIdFormat('billing:%s')->setFieldNameFormat('billing[%s]')->toHtml() ?>
155
+ </li>
156
+ <?php endif ?>
157
+
158
+ <li class="fields" id="register-customer-password">
159
+ <div class="field">
160
+ <label for="billing:customer_password" class="required"><em>*</em><?php echo $this->__('Password') ?></label>
161
+ <div class="input-box">
162
+ <input type="password" name="billing[customer_password]" id="billing:customer_password" title="<?php echo $this->__('Password') ?>" class="input-text required-entry validate-password" />
163
+ </div>
164
+ </div>
165
+ <div class="field">
166
+ <label for="billing:confirm_password" class="required"><em>*</em><?php echo $this->__('Confirm Password') ?></label>
167
+ <div class="input-box">
168
+ <input type="password" name="billing[confirm_password]" title="<?php echo $this->__('Confirm Password') ?>" id="billing:confirm_password" class="input-text required-entry validate-cpassword" />
169
+ </div>
170
+ </div>
171
+ </li>
172
+ <?php endif; ?>
173
+ <?php if ($this->isCustomerLoggedIn() && $this->customerHasAddresses()):?>
174
+ <li class="control">
175
+ <input type="checkbox" name="billing[save_in_address_book]" value="1" title="<?php echo $this->__('Save in address book') ?>" id="billing:save_in_address_book" onchange="if(window.shipping) shipping.setSameAsBilling(false);"<?php if ($this->getAddress()->getSaveInAddressBook()):?> checked="checked"<?php endif;?> class="checkbox" /><label for="billing:save_in_address_book"><?php echo $this->__('Save in address book') ?></label>
176
+ </li>
177
+ <?php else:?>
178
+ <li class="no-display"><input type="hidden" name="billing[save_in_address_book]" value="1" /></li>
179
+ <?php endif; ?>
180
+ <?php echo $this->getChildHtml('form.additional.info'); ?>
181
+ </ul>
182
+ </fieldset>
183
+ </li>
184
+ <?php /* Extensions placeholder */ ?>
185
+ <?php echo $this->getChildHtml('checkout.onepage.billing.extra')?>
186
+ <?php if ($this->canShip()): ?>
187
+ <li class="control">
188
+ <input type="radio" name="billing[use_for_shipping]" id="billing:use_for_shipping_yes" value="1"<?php if ($this->isUseBillingAddressForShipping()) {?> checked="checked"<?php }?> title="<?php echo $this->__('Ship to this address') ?>" onclick="$('shipping:same_as_billing').checked = true;" class="radio" /><label for="billing:use_for_shipping_yes"><?php echo $this->__('Ship to this address') ?></label></li>
189
+ <li class="control">
190
+ <input type="radio" name="billing[use_for_shipping]" id="billing:use_for_shipping_no" value="0"<?php if (!$this->isUseBillingAddressForShipping()) {?> checked="checked"<?php }?> title="<?php echo $this->__('Ship to different address') ?>" onclick="$('shipping:same_as_billing').checked = false;" class="radio" /><label for="billing:use_for_shipping_no"><?php echo $this->__('Ship to different address') ?></label>
191
+ </li>
192
+ <?php if (Mage::getStoreConfig('carriers/ibstorepickup/active') && Mage::getStoreConfig('carriers/ibstorepickup/accept')) : ?>
193
+ <li class="control">
194
+ <input type="radio" name="billing[use_for_shipping]" id="billing:use_for_shipping_point" value="1"<?php if (Mage::helper('ibstorepickup')->getUseMethod()) : ?> checked="checked" <?php endif ?> onclick="$('shipping:same_as_billing').checked = true;" class="radio" />
195
+ <label for="billing:use_for_shipping_point"><?php echo $this->__('IndaBox Store Pickup') ?></label>
196
+ </li>
197
+ <?php endif ?>
198
+ <?php endif; ?>
199
+ </ul>
200
+ <?php if (!$this->canShip()): ?>
201
+ <input type="hidden" name="billing[use_for_shipping]" value="1" />
202
+ <?php endif; ?>
203
+ <div class="buttons-set" id="billing-buttons-container">
204
+ <p class="required"><?php echo $this->__('* Required Fields') ?></p>
205
+ <button type="button" title="<?php echo $this->__('Continue') ?>" class="button" onclick="billing.save()"><span><span><?php echo $this->__('Continue') ?></span></span></button>
206
+ <span class="please-wait" id="billing-please-wait" style="display:none;">
207
+ <img src="<?php echo $this->getSkinUrl('images/opc-ajax-loader.gif') ?>" alt="<?php echo $this->__('Loading next step...') ?>" title="<?php echo $this->__('Loading next step...') ?>" class="v-middle" /> <?php echo $this->__('Loading next step...') ?>
208
+ </span>
209
+ </div>
210
+ </fieldset>
211
+ </form>
212
+ <script type="text/javascript">
213
+ //<![CDATA[
214
+ var billing = new Billing('co-billing-form', '<?php echo $this->getUrl('checkout/onepage/getAddress') ?>address/', '<?php echo $this->getUrl('checkout/onepage/saveBilling') ?>');
215
+ var billingForm = new VarienForm('co-billing-form');
216
+
217
+ //billingForm.setElementsRelation('billing:country_id', 'billing:region', '<?php echo $this->getUrl('directory/json/childRegion') ?>', '<?php echo $this->__('Select State/Province...') ?>');
218
+ $('billing-address-select') && billing.newAddress(!$('billing-address-select').value);
219
+
220
+ var billingRegionUpdater = new RegionUpdater('billing:country_id', 'billing:region', 'billing:region_id', <?php echo $this->helper('directory')->getRegionJson() ?>, undefined, 'billing:postcode');
221
+ //]]>
222
+ </script>
223
+ <?php if ($this->canShip() && Mage::getStoreConfig('carriers/ibstorepickup/active')): ?>
224
+ <script type="text/javascript">
225
+ //<![CDATA[
226
+ var ibStorePickup = new IndaboxStorePickup(
227
+ '<?php echo Mage::helper('ibstorepickup')->getChangeMethodUrl(); ?>',
228
+ '<?php echo Mage::helper('ibstorepickup')->getSearchUrl(); ?>',
229
+ '<?php echo Mage::helper('ibstorepickup')->getLocationUrl(); ?>',
230
+ '<?php echo Mage::helper('ibstorepickup')->getMediaUrl(); ?>'
231
+ );
232
+
233
+ Event.observe('billing:use_for_shipping_point', 'click', function(event){
234
+ ibStorePickup.setUseStorePickup(true);
235
+ });
236
+
237
+ Event.observe('billing:use_for_shipping_yes', 'click', function(event){
238
+ ibStorePickup.setUseStorePickup(false);
239
+ });
240
+
241
+ Event.observe('billing:use_for_shipping_no', 'click', function(event){
242
+ ibStorePickup.setUseStorePickup(false);
243
+ });
244
+ //]]>
245
+ </script>
246
+ <?php endif ?>
app/design/frontend/base/default/template/ibstorepickup/map.phtml ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="indabox">
2
+ <div id="indabox_conditions">
3
+ <iframe src="<?php echo $this->getTosUrl() ?>">
4
+ <p><?php echo $this->__('Your browser does not support iframes.') ?></p>
5
+ </iframe>
6
+ <div style="clear: both"></div>
7
+ <p class="checkbox">
8
+ <input type="checkbox" name="indabox_accept_terms" id="indabox_accept_terms" value="1" autocomplete="off" class="required" />
9
+ <label for="indabox_accept_terms"><?php echo $this->__('I accept IndaBox terms and conditions') ?></label>
10
+ </p>
11
+ </div>
12
+ <div id="indabox_map" style="display: none;">
13
+ <h3><?php echo $this->__('Search for pick-up points') ?></h3>
14
+ <p><?php echo $this->__('You are viewing the collection points closest to the address below. If you want, you can enter a different address.') ?></p>
15
+ <div class="address">
16
+ <input type="text" id="indabox_search_address" value="<?php echo $this->htmlEscape($this->getBillingAddress()) ?>" size="50" />
17
+ <label for="indabox_search_radius"><?php echo $this->__('Radius') ?></label>
18
+ <select id="indabox_search_radius">
19
+ <option value="1">1 km</option>
20
+ <option value="3" selected="selected">3 km</option>
21
+ <option value="10">10 km</option>
22
+ <option value="30">30 km</option>
23
+ <option value="50">50 km</option>
24
+ </select>
25
+ <button id="indabox_search" class="button"><span><span><?php echo $this->__('Search') ?></span></span></button>
26
+ <img src="<?php echo $this->getSkinUrl('indabox/images/opc-ajax-loader.gif') ?>" alt="<?php echo $this->__('Loading') ?>" />
27
+ </div>
28
+ <div id="indabox_google_map"></div>
29
+ </div>
30
+ <div id="indabox_point" style="display: none;">
31
+ <input type="hidden" name="indabox_point_id" id="indabox_point_id" value="0" />
32
+ <h3><?php echo $this->__('Selected pick-up point') ?></h3>
33
+ <h5 id="indabox_point_name"></h5>
34
+ <p id="indabox_point_address"></p>
35
+ </div>
36
+ </div>
37
+ <script type="text/javascript">
38
+ //<![CDATA[
39
+
40
+ Event.observe('indabox_accept_terms', 'click', function (event) {
41
+ $('indabox_conditions').hide();
42
+ $('indabox_map').show();
43
+
44
+ ibStorePickup.initGoogleMap('<?php echo $this->getGoogleMaps() ?>', 'indabox_google_map');
45
+ ibStorePickup.onInit = function () {
46
+ ibStorePickup.search($('indabox_search_address').value, $('indabox_search_radius').value);
47
+ };
48
+ ibStorePickup.onSearchStart = function () {
49
+ $('indabox_search').addClassName('in-progress').addClassName('disabled');
50
+ };
51
+ ibStorePickup.onSearchEnd = function (points) {
52
+ $('indabox_search').removeClassName('in-progress').removeClassName('disabled');
53
+ if ( ! points.length)
54
+ alert('<?php echo $this->__('No points found around given location') ?>');
55
+ };
56
+ ibStorePickup.onSelectPoint = function (point) {
57
+ $('indabox_point').show();
58
+ $('indabox_point_id').value = point.id;
59
+ $('indabox_point_name').innerHTML = point.name;
60
+ $('indabox_point_address').innerHTML = point.street
61
+ + '<br />' + point.postcode
62
+ + ' ' + point.city
63
+ + ' ' + point.region
64
+ //+ ' ' + point.country_id
65
+ ;
66
+ };
67
+ });
68
+
69
+ Event.observe('indabox_search', 'click', function (event) {
70
+ event.preventDefault();
71
+ if ($(this).hasClassName('in-progress')) {
72
+ alert('<?php echo $this->__('Another search is already running') ?>');
73
+ return;
74
+ }
75
+
76
+ ibStorePickup.search($('indabox_search_address').value, $('indabox_search_radius').value);
77
+ });
78
+ //]]>
79
+ </script>
app/etc/modules/IndaBox_StorePickup.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <IndaBox_StorePickup>
5
+ <active>true</active>
6
+ <codePool>local</codePool>
7
+ </IndaBox_StorePickup>
8
+ </modules>
9
+ </config>
app/locale/en_US/IndaBox_StorePickup.csv ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "IndaBox","IndaBox"
2
+ "Title","Title"
3
+ "Cost","Cost"
4
+ "Maximum Subtotal","Maximum Subtotal"
5
+ "Maximum Weight","Maximum Weight"
6
+ "Terms and Conditions Url","Terms and Conditions Url"
7
+ "Google Maps API Key","Google Maps API Key"
8
+ "Applicable payments","Applicable payments"
9
+ "Specific payments","Specific payments"
10
+ "Point","Point"
11
+ "Please specify Merchant ID","Please specify Merchant ID"
12
+ "Please specify API Key","Please specify API Key"
13
+ "Could not communicate with server","Could not communicate with server"
14
+ "Invalid server reply","Invalid server reply"
15
+ "Could not submit order","Could not submit order"
16
+ "All Allowed Payments","All Allowed Payments"
17
+ "Specific Payments","Specific Payments"
18
+ "Invalid search parameters.","Invalid search parameters."
19
+ "Error while communicating with IndaBox. Please retry later.","Error while communicating with IndaBox. Please retry later."
20
+ "Location does not found","Location does not found"
21
+ "Your browser does not support iframes.","Your browser does not support iframes."
22
+ "I accept IndaBox terms and conditions","I accept IndaBox terms and conditions"
23
+ "Search for pick-up points","Search for pick-up points"
24
+ "You are viewing the collection points closest to the address below. If you want, you can enter a different address.","You are viewing the collection points closest to the address below. If you want, you can enter a different address."
25
+ "Radius","Radius"
26
+ "Loading","Loading"
27
+ "Selected pick-up point","Selected pick-up point"
28
+ "No points found around given location","No points found around given location"
29
+ "Another search is already running","Another search is already running"
30
+ "IndaBox Store Pickup","IndaBox Store Pickup"
31
+ "You should accept IndaBox terms and conditions","You should accept IndaBox terms and conditions"
32
+ "You should select one of available pick-up points to continue","You should select one of available pick-up points to continue"
33
+ "Distance","Distance"
34
+ "Select this pick-up point","Select this pick-up point"
35
+ "I accept <a href=""http://www.indabox.it/termini-eco/"" target=""_blank"">IndaBox Terms of Service</a>","I accept <a href=""http://www.indabox.it/termini-eco/"" target=""_blank"">IndaBox Terms of Service</a>"
36
+ "Accept TOS","Accept TOS"
37
+ "IndaBox Point","IndaBox Point"
38
+ "For all the information on installing and configuring the module and IndaBox Merchant Account, you can refer to <a href=""%s"" target=""_blank"">this page</a>","For all the information on installing and configuring the module and IndaBox Merchant Account, you can refer to <a href=""%s"" target=""_blank"">this page</a>"
app/locale/it_IT/IndaBox_StorePickup.csv ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "IndaBox","IndaBox"
2
+ "Title","Titolo"
3
+ "Cost","Costo"
4
+ "Maximum Subtotal","Subtotale Massimo"
5
+ "Maximum Weight","Peso Massimo"
6
+ "Terms and Conditions Url","Url Termini e Condizioni"
7
+ "Google Maps API Key","Chiave API Google Maps"
8
+ "Applicable payments","Pagamenti Applicabili"
9
+ "Point","Punto"
10
+ "Please specify Merchant ID","Specificare il Merchant ID"
11
+ "Please specify API Key","Specificare l´API Key"
12
+ "Could not communicate with server","Impossibile comunicare col server"
13
+ "Invalid server reply","Risposta server non valida"
14
+ "Could not submit order","Non è stato possibile inviare l´ordine."
15
+ "All Allowed Payments","Tutti i Pagamenti Consentiti"
16
+ "Specific Payments","Specifici Pagamenti"
17
+ "Invalid search parameters.","Parametri di ricerca non validi."
18
+ "Error while communicating with IndaBox. Please retry later.","Si è verificato un errore durante la comunicazione con IndaBox. Riprovare a breve."
19
+ "Location does not found","Location non trovata"
20
+ "Your browser does not support iframes.","Il tuo browser non supporta gli iframe."
21
+ "Loading","Caricando"
22
+ "No points found around given location","Non è stato trovato alcun punto IndaBox nelle vicinanze della location inserita."
23
+ "Another search is already running","Vi è già un´altra ricerca in corso."
24
+ "You should accept IndaBox terms and conditions","E´ necessario accettare i termini e condizioni IndaBox"
25
+ "Distance","Distanza"
26
+ "Selected pick-up point","Hai selezionato il seguente punto di ritiro"
27
+ "You should select one of available pick-up points to continue","Selezionare un punto di ritiro dalla mappa prima di continuare"
28
+ "Select this pick-up point","Seleziona questo punto di ritiro"
29
+ "Radius","Raggio"
30
+ "IndaBox Store Pickup","Ricevi il tuo acquisto in uno dei punti di ritiro IndaBox (<a target=""_blank"" href=""http://www.indabox.it"">www.indabox.it</a>)"
31
+ "I accept IndaBox terms and conditions","Accetto i termini di utilizzo del servizio indicati"
32
+ "Search for pick-up points","Cerca un punto di ritiro"
33
+ "You are viewing the collection points closest to the address below. If you want, you can enter a different address.","Stai visualizzando i punti di ritiro vicini al seguente indirizzo. <br/>Se vuoi, puoi inserire un indirizzo differente."
34
+ "I accept <a href=""http://www.indabox.it/termini-eco/"" target=""_blank"">IndaBox Terms of Service</a>","Accetto <a href=""http://www.indabox.it/termini-eco/"" target=""_blank"">IndaBox Termini di Servizio</a>"
35
+ "Accept TOS","Accettare Termini di Servizio"
36
+ "IndaBox Point","IndaBox Point"
37
+ "For all the information on installing and configuring the module and IndaBox Merchant Account, you can refer to <a href=""%s"" target=""_blank"">this page</a>","Per tutte le informazioni sull´installazione e la configurazione del modulo e IndaBox Merchant Account, è possibile fare riferimento a <a href=""%s"" target=""_blank"">questa pagina</a>"
38
+
js/indabox/markerclusterer.js ADDED
@@ -0,0 +1,1310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ==ClosureCompiler==
2
+ // @compilation_level ADVANCED_OPTIMIZATIONS
3
+ // @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3_3.js
4
+ // ==/ClosureCompiler==
5
+
6
+ /**
7
+ * @name MarkerClusterer for Google Maps v3
8
+ * @version version 1.0.1
9
+ * @author Luke Mahe
10
+ * @fileoverview
11
+ * The library creates and manages per-zoom-level clusters for large amounts of
12
+ * markers.
13
+ * <br/>
14
+ * This is a v3 implementation of the
15
+ * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/"
16
+ * >v2 MarkerClusterer</a>.
17
+ */
18
+
19
+ /**
20
+ * Licensed under the Apache License, Version 2.0 (the "License");
21
+ * you may not use this file except in compliance with the License.
22
+ * You may obtain a copy of the License at
23
+ *
24
+ * http://www.apache.org/licenses/LICENSE-2.0
25
+ *
26
+ * Unless required by applicable law or agreed to in writing, software
27
+ * distributed under the License is distributed on an "AS IS" BASIS,
28
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29
+ * See the License for the specific language governing permissions and
30
+ * limitations under the License.
31
+ */
32
+
33
+
34
+ /**
35
+ * A Marker Clusterer that clusters markers.
36
+ *
37
+ * @param {google.maps.Map} map The Google map to attach to.
38
+ * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to
39
+ * the cluster.
40
+ * @param {Object=} opt_options support the following options:
41
+ * 'gridSize': (number) The grid size of a cluster in pixels.
42
+ * 'maxZoom': (number) The maximum zoom level that a marker can be part of a
43
+ * cluster.
44
+ * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a
45
+ * cluster is to zoom into it.
46
+ * 'averageCenter': (boolean) Wether the center of each cluster should be
47
+ * the average of all markers in the cluster.
48
+ * 'minimumClusterSize': (number) The minimum number of markers to be in a
49
+ * cluster before the markers are hidden and a count
50
+ * is shown.
51
+ * 'styles': (object) An object that has style properties:
52
+ * 'url': (string) The image url.
53
+ * 'height': (number) The image height.
54
+ * 'width': (number) The image width.
55
+ * 'anchor': (Array) The anchor position of the label text.
56
+ * 'textColor': (string) The text color.
57
+ * 'textSize': (number) The text size.
58
+ * 'backgroundPosition': (string) The position of the backgound x, y.
59
+ * @constructor
60
+ * @extends google.maps.OverlayView
61
+ */
62
+ function MarkerClusterer(map, opt_markers, opt_options) {
63
+ // MarkerClusterer implements google.maps.OverlayView interface. We use the
64
+ // extend function to extend MarkerClusterer with google.maps.OverlayView
65
+ // because it might not always be available when the code is defined so we
66
+ // look for it at the last possible moment. If it doesn't exist now then
67
+ // there is no point going ahead :)
68
+ this.extend(MarkerClusterer, google.maps.OverlayView);
69
+ this.map_ = map;
70
+
71
+ /**
72
+ * @type {Array.<google.maps.Marker>}
73
+ * @private
74
+ */
75
+ this.markers_ = [];
76
+
77
+ /**
78
+ * @type {Array.<Cluster>}
79
+ */
80
+ this.clusters_ = [];
81
+
82
+ this.sizes = [53, 56, 66, 78, 90];
83
+
84
+ /**
85
+ * @private
86
+ */
87
+ this.styles_ = [];
88
+
89
+ /**
90
+ * @type {boolean}
91
+ * @private
92
+ */
93
+ this.ready_ = false;
94
+
95
+ var options = opt_options || {};
96
+
97
+ /**
98
+ * @type {number}
99
+ * @private
100
+ */
101
+ this.gridSize_ = options['gridSize'] || 60;
102
+
103
+ /**
104
+ * @private
105
+ */
106
+ this.minClusterSize_ = options['minimumClusterSize'] || 2;
107
+
108
+
109
+ /**
110
+ * @type {?number}
111
+ * @private
112
+ */
113
+ this.maxZoom_ = options['maxZoom'] || null;
114
+
115
+ this.styles_ = options['styles'] || [];
116
+
117
+ /**
118
+ * @type {string}
119
+ * @private
120
+ */
121
+ this.imagePath_ = options['imagePath'] ||
122
+ this.MARKER_CLUSTER_IMAGE_PATH_;
123
+
124
+ /**
125
+ * @type {string}
126
+ * @private
127
+ */
128
+ this.imageExtension_ = options['imageExtension'] ||
129
+ this.MARKER_CLUSTER_IMAGE_EXTENSION_;
130
+
131
+ /**
132
+ * @type {boolean}
133
+ * @private
134
+ */
135
+ this.zoomOnClick_ = true;
136
+
137
+ if (options['zoomOnClick'] != undefined) {
138
+ this.zoomOnClick_ = options['zoomOnClick'];
139
+ }
140
+
141
+ /**
142
+ * @type {boolean}
143
+ * @private
144
+ */
145
+ this.averageCenter_ = false;
146
+
147
+ if (options['averageCenter'] != undefined) {
148
+ this.averageCenter_ = options['averageCenter'];
149
+ }
150
+
151
+ this.setupStyles_();
152
+
153
+ this.setMap(map);
154
+
155
+ /**
156
+ * @type {number}
157
+ * @private
158
+ */
159
+ this.prevZoom_ = this.map_.getZoom();
160
+
161
+ // Add the map event listeners
162
+ var that = this;
163
+ google.maps.event.addListener(this.map_, 'zoom_changed', function() {
164
+ // Determines map type and prevent illegal zoom levels
165
+ var zoom = that.map_.getZoom();
166
+ var minZoom = that.map_.minZoom || 0;
167
+ var maxZoom = Math.min(that.map_.maxZoom || 100,
168
+ that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom);
169
+ zoom = Math.min(Math.max(zoom,minZoom),maxZoom);
170
+
171
+ if (that.prevZoom_ != zoom) {
172
+ that.prevZoom_ = zoom;
173
+ that.resetViewport();
174
+ }
175
+ });
176
+
177
+ google.maps.event.addListener(this.map_, 'idle', function() {
178
+ that.redraw();
179
+ });
180
+
181
+ // Finally, add the markers
182
+ if (opt_markers && (opt_markers.length || Object.keys(opt_markers).length)) {
183
+ this.addMarkers(opt_markers, false);
184
+ }
185
+ }
186
+
187
+
188
+ /**
189
+ * The marker cluster image path.
190
+ *
191
+ * @type {string}
192
+ * @private
193
+ */
194
+ MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ =
195
+ 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' +
196
+ 'images/m';
197
+
198
+
199
+ /**
200
+ * The marker cluster image path.
201
+ *
202
+ * @type {string}
203
+ * @private
204
+ */
205
+ MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png';
206
+
207
+
208
+ /**
209
+ * Extends a objects prototype by anothers.
210
+ *
211
+ * @param {Object} obj1 The object to be extended.
212
+ * @param {Object} obj2 The object to extend with.
213
+ * @return {Object} The new extended object.
214
+ * @ignore
215
+ */
216
+ MarkerClusterer.prototype.extend = function(obj1, obj2) {
217
+ return (function(object) {
218
+ for (var property in object.prototype) {
219
+ this.prototype[property] = object.prototype[property];
220
+ }
221
+ return this;
222
+ }).apply(obj1, [obj2]);
223
+ };
224
+
225
+
226
+ /**
227
+ * Implementaion of the interface method.
228
+ * @ignore
229
+ */
230
+ MarkerClusterer.prototype.onAdd = function() {
231
+ this.setReady_(true);
232
+ };
233
+
234
+ /**
235
+ * Implementaion of the interface method.
236
+ * @ignore
237
+ */
238
+ MarkerClusterer.prototype.draw = function() {};
239
+
240
+ /**
241
+ * Sets up the styles object.
242
+ *
243
+ * @private
244
+ */
245
+ MarkerClusterer.prototype.setupStyles_ = function() {
246
+ if (this.styles_.length) {
247
+ return;
248
+ }
249
+
250
+ for (var i = 0, size; size = this.sizes[i]; i++) {
251
+ this.styles_.push({
252
+ url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_,
253
+ height: size,
254
+ width: size
255
+ });
256
+ }
257
+ };
258
+
259
+ /**
260
+ * Fit the map to the bounds of the markers in the clusterer.
261
+ */
262
+ MarkerClusterer.prototype.fitMapToMarkers = function() {
263
+ var markers = this.getMarkers();
264
+ var bounds = new google.maps.LatLngBounds();
265
+ for (var i = 0, marker; marker = markers[i]; i++) {
266
+ bounds.extend(marker.getPosition());
267
+ }
268
+
269
+ this.map_.fitBounds(bounds);
270
+ };
271
+
272
+
273
+ /**
274
+ * Sets the styles.
275
+ *
276
+ * @param {Object} styles The style to set.
277
+ */
278
+ MarkerClusterer.prototype.setStyles = function(styles) {
279
+ this.styles_ = styles;
280
+ };
281
+
282
+
283
+ /**
284
+ * Gets the styles.
285
+ *
286
+ * @return {Object} The styles object.
287
+ */
288
+ MarkerClusterer.prototype.getStyles = function() {
289
+ return this.styles_;
290
+ };
291
+
292
+
293
+ /**
294
+ * Whether zoom on click is set.
295
+ *
296
+ * @return {boolean} True if zoomOnClick_ is set.
297
+ */
298
+ MarkerClusterer.prototype.isZoomOnClick = function() {
299
+ return this.zoomOnClick_;
300
+ };
301
+
302
+ /**
303
+ * Whether average center is set.
304
+ *
305
+ * @return {boolean} True if averageCenter_ is set.
306
+ */
307
+ MarkerClusterer.prototype.isAverageCenter = function() {
308
+ return this.averageCenter_;
309
+ };
310
+
311
+
312
+ /**
313
+ * Returns the array of markers in the clusterer.
314
+ *
315
+ * @return {Array.<google.maps.Marker>} The markers.
316
+ */
317
+ MarkerClusterer.prototype.getMarkers = function() {
318
+ return this.markers_;
319
+ };
320
+
321
+
322
+ /**
323
+ * Returns the number of markers in the clusterer
324
+ *
325
+ * @return {Number} The number of markers.
326
+ */
327
+ MarkerClusterer.prototype.getTotalMarkers = function() {
328
+ return this.markers_.length;
329
+ };
330
+
331
+
332
+ /**
333
+ * Sets the max zoom for the clusterer.
334
+ *
335
+ * @param {number} maxZoom The max zoom level.
336
+ */
337
+ MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
338
+ this.maxZoom_ = maxZoom;
339
+ };
340
+
341
+
342
+ /**
343
+ * Gets the max zoom for the clusterer.
344
+ *
345
+ * @return {number} The max zoom level.
346
+ */
347
+ MarkerClusterer.prototype.getMaxZoom = function() {
348
+ return this.maxZoom_;
349
+ };
350
+
351
+
352
+ /**
353
+ * The function for calculating the cluster icon image.
354
+ *
355
+ * @param {Array.<google.maps.Marker>} markers The markers in the clusterer.
356
+ * @param {number} numStyles The number of styles available.
357
+ * @return {Object} A object properties: 'text' (string) and 'index' (number).
358
+ * @private
359
+ */
360
+ MarkerClusterer.prototype.calculator_ = function(markers, numStyles) {
361
+ var index = 0;
362
+ var count = markers.length;
363
+ var dv = count;
364
+ while (dv !== 0) {
365
+ dv = parseInt(dv / 10, 10);
366
+ index++;
367
+ }
368
+
369
+ index = Math.min(index, numStyles);
370
+ return {
371
+ text: count,
372
+ index: index
373
+ };
374
+ };
375
+
376
+
377
+ /**
378
+ * Set the calculator function.
379
+ *
380
+ * @param {function(Array, number)} calculator The function to set as the
381
+ * calculator. The function should return a object properties:
382
+ * 'text' (string) and 'index' (number).
383
+ *
384
+ */
385
+ MarkerClusterer.prototype.setCalculator = function(calculator) {
386
+ this.calculator_ = calculator;
387
+ };
388
+
389
+
390
+ /**
391
+ * Get the calculator function.
392
+ *
393
+ * @return {function(Array, number)} the calculator function.
394
+ */
395
+ MarkerClusterer.prototype.getCalculator = function() {
396
+ return this.calculator_;
397
+ };
398
+
399
+
400
+ /**
401
+ * Add an array of markers to the clusterer.
402
+ *
403
+ * @param {Array.<google.maps.Marker>} markers The markers to add.
404
+ * @param {boolean=} opt_nodraw Whether to redraw the clusters.
405
+ */
406
+ MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) {
407
+ if (markers.length) {
408
+ for (var i = 0, marker; marker = markers[i]; i++) {
409
+ this.pushMarkerTo_(marker);
410
+ }
411
+ } else if (Object.keys(markers).length) {
412
+ for (var marker in markers) {
413
+ this.pushMarkerTo_(markers[marker]);
414
+ }
415
+ }
416
+ if (!opt_nodraw) {
417
+ this.redraw();
418
+ }
419
+ };
420
+
421
+
422
+ /**
423
+ * Pushes a marker to the clusterer.
424
+ *
425
+ * @param {google.maps.Marker} marker The marker to add.
426
+ * @private
427
+ */
428
+ MarkerClusterer.prototype.pushMarkerTo_ = function(marker) {
429
+ marker.isAdded = false;
430
+ if (marker['draggable']) {
431
+ // If the marker is draggable add a listener so we update the clusters on
432
+ // the drag end.
433
+ var that = this;
434
+ google.maps.event.addListener(marker, 'dragend', function() {
435
+ marker.isAdded = false;
436
+ that.repaint();
437
+ });
438
+ }
439
+ this.markers_.push(marker);
440
+ };
441
+
442
+
443
+ /**
444
+ * Adds a marker to the clusterer and redraws if needed.
445
+ *
446
+ * @param {google.maps.Marker} marker The marker to add.
447
+ * @param {boolean=} opt_nodraw Whether to redraw the clusters.
448
+ */
449
+ MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) {
450
+ this.pushMarkerTo_(marker);
451
+ if (!opt_nodraw) {
452
+ this.redraw();
453
+ }
454
+ };
455
+
456
+
457
+ /**
458
+ * Removes a marker and returns true if removed, false if not
459
+ *
460
+ * @param {google.maps.Marker} marker The marker to remove
461
+ * @return {boolean} Whether the marker was removed or not
462
+ * @private
463
+ */
464
+ MarkerClusterer.prototype.removeMarker_ = function(marker) {
465
+ var index = -1;
466
+ if (this.markers_.indexOf) {
467
+ index = this.markers_.indexOf(marker);
468
+ } else {
469
+ for (var i = 0, m; m = this.markers_[i]; i++) {
470
+ if (m == marker) {
471
+ index = i;
472
+ break;
473
+ }
474
+ }
475
+ }
476
+
477
+ if (index == -1) {
478
+ // Marker is not in our list of markers.
479
+ return false;
480
+ }
481
+
482
+ marker.setMap(null);
483
+
484
+ this.markers_.splice(index, 1);
485
+
486
+ return true;
487
+ };
488
+
489
+
490
+ /**
491
+ * Remove a marker from the cluster.
492
+ *
493
+ * @param {google.maps.Marker} marker The marker to remove.
494
+ * @param {boolean=} opt_nodraw Optional boolean to force no redraw.
495
+ * @return {boolean} True if the marker was removed.
496
+ */
497
+ MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) {
498
+ var removed = this.removeMarker_(marker);
499
+
500
+ if (!opt_nodraw && removed) {
501
+ this.resetViewport();
502
+ this.redraw();
503
+ return true;
504
+ } else {
505
+ return false;
506
+ }
507
+ };
508
+
509
+
510
+ /**
511
+ * Removes an array of markers from the cluster.
512
+ *
513
+ * @param {Array.<google.maps.Marker>} markers The markers to remove.
514
+ * @param {boolean=} opt_nodraw Optional boolean to force no redraw.
515
+ */
516
+ MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) {
517
+ var removed = false;
518
+
519
+ for (var i = 0, marker; marker = markers[i]; i++) {
520
+ var r = this.removeMarker_(marker);
521
+ removed = removed || r;
522
+ }
523
+
524
+ if (!opt_nodraw && removed) {
525
+ this.resetViewport();
526
+ this.redraw();
527
+ return true;
528
+ }
529
+ };
530
+
531
+
532
+ /**
533
+ * Sets the clusterer's ready state.
534
+ *
535
+ * @param {boolean} ready The state.
536
+ * @private
537
+ */
538
+ MarkerClusterer.prototype.setReady_ = function(ready) {
539
+ if (!this.ready_) {
540
+ this.ready_ = ready;
541
+ this.createClusters_();
542
+ }
543
+ };
544
+
545
+
546
+ /**
547
+ * Returns the number of clusters in the clusterer.
548
+ *
549
+ * @return {number} The number of clusters.
550
+ */
551
+ MarkerClusterer.prototype.getTotalClusters = function() {
552
+ return this.clusters_.length;
553
+ };
554
+
555
+
556
+ /**
557
+ * Returns the google map that the clusterer is associated with.
558
+ *
559
+ * @return {google.maps.Map} The map.
560
+ */
561
+ MarkerClusterer.prototype.getMap = function() {
562
+ return this.map_;
563
+ };
564
+
565
+
566
+ /**
567
+ * Sets the google map that the clusterer is associated with.
568
+ *
569
+ * @param {google.maps.Map} map The map.
570
+ */
571
+ MarkerClusterer.prototype.setMap = function(map) {
572
+ this.map_ = map;
573
+ };
574
+
575
+
576
+ /**
577
+ * Returns the size of the grid.
578
+ *
579
+ * @return {number} The grid size.
580
+ */
581
+ MarkerClusterer.prototype.getGridSize = function() {
582
+ return this.gridSize_;
583
+ };
584
+
585
+
586
+ /**
587
+ * Sets the size of the grid.
588
+ *
589
+ * @param {number} size The grid size.
590
+ */
591
+ MarkerClusterer.prototype.setGridSize = function(size) {
592
+ this.gridSize_ = size;
593
+ };
594
+
595
+
596
+ /**
597
+ * Returns the min cluster size.
598
+ *
599
+ * @return {number} The grid size.
600
+ */
601
+ MarkerClusterer.prototype.getMinClusterSize = function() {
602
+ return this.minClusterSize_;
603
+ };
604
+
605
+ /**
606
+ * Sets the min cluster size.
607
+ *
608
+ * @param {number} size The grid size.
609
+ */
610
+ MarkerClusterer.prototype.setMinClusterSize = function(size) {
611
+ this.minClusterSize_ = size;
612
+ };
613
+
614
+
615
+ /**
616
+ * Extends a bounds object by the grid size.
617
+ *
618
+ * @param {google.maps.LatLngBounds} bounds The bounds to extend.
619
+ * @return {google.maps.LatLngBounds} The extended bounds.
620
+ */
621
+ MarkerClusterer.prototype.getExtendedBounds = function(bounds) {
622
+ var projection = this.getProjection();
623
+
624
+ // Turn the bounds into latlng.
625
+ var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
626
+ bounds.getNorthEast().lng());
627
+ var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
628
+ bounds.getSouthWest().lng());
629
+
630
+ // Convert the points to pixels and the extend out by the grid size.
631
+ var trPix = projection.fromLatLngToDivPixel(tr);
632
+ trPix.x += this.gridSize_;
633
+ trPix.y -= this.gridSize_;
634
+
635
+ var blPix = projection.fromLatLngToDivPixel(bl);
636
+ blPix.x -= this.gridSize_;
637
+ blPix.y += this.gridSize_;
638
+
639
+ // Convert the pixel points back to LatLng
640
+ var ne = projection.fromDivPixelToLatLng(trPix);
641
+ var sw = projection.fromDivPixelToLatLng(blPix);
642
+
643
+ // Extend the bounds to contain the new bounds.
644
+ bounds.extend(ne);
645
+ bounds.extend(sw);
646
+
647
+ return bounds;
648
+ };
649
+
650
+
651
+ /**
652
+ * Determins if a marker is contained in a bounds.
653
+ *
654
+ * @param {google.maps.Marker} marker The marker to check.
655
+ * @param {google.maps.LatLngBounds} bounds The bounds to check against.
656
+ * @return {boolean} True if the marker is in the bounds.
657
+ * @private
658
+ */
659
+ MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) {
660
+ return bounds.contains(marker.getPosition());
661
+ };
662
+
663
+
664
+ /**
665
+ * Clears all clusters and markers from the clusterer.
666
+ */
667
+ MarkerClusterer.prototype.clearMarkers = function() {
668
+ this.resetViewport(true);
669
+
670
+ // Set the markers a empty array.
671
+ this.markers_ = [];
672
+ };
673
+
674
+
675
+ /**
676
+ * Clears all existing clusters and recreates them.
677
+ * @param {boolean} opt_hide To also hide the marker.
678
+ */
679
+ MarkerClusterer.prototype.resetViewport = function(opt_hide) {
680
+ // Remove all the clusters
681
+ for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {
682
+ cluster.remove();
683
+ }
684
+
685
+ // Reset the markers to not be added and to be invisible.
686
+ for (var i = 0, marker; marker = this.markers_[i]; i++) {
687
+ marker.isAdded = false;
688
+ if (opt_hide) {
689
+ marker.setMap(null);
690
+ }
691
+ }
692
+
693
+ this.clusters_ = [];
694
+ };
695
+
696
+ /**
697
+ *
698
+ */
699
+ MarkerClusterer.prototype.repaint = function() {
700
+ var oldClusters = this.clusters_.slice();
701
+ this.clusters_.length = 0;
702
+ this.resetViewport();
703
+ this.redraw();
704
+
705
+ // Remove the old clusters.
706
+ // Do it in a timeout so the other clusters have been drawn first.
707
+ window.setTimeout(function() {
708
+ for (var i = 0, cluster; cluster = oldClusters[i]; i++) {
709
+ cluster.remove();
710
+ }
711
+ }, 0);
712
+ };
713
+
714
+
715
+ /**
716
+ * Redraws the clusters.
717
+ */
718
+ MarkerClusterer.prototype.redraw = function() {
719
+ this.createClusters_();
720
+ };
721
+
722
+
723
+ /**
724
+ * Calculates the distance between two latlng locations in km.
725
+ * @see http://www.movable-type.co.uk/scripts/latlong.html
726
+ *
727
+ * @param {google.maps.LatLng} p1 The first lat lng point.
728
+ * @param {google.maps.LatLng} p2 The second lat lng point.
729
+ * @return {number} The distance between the two points in km.
730
+ * @private
731
+ */
732
+ MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) {
733
+ if (!p1 || !p2) {
734
+ return 0;
735
+ }
736
+
737
+ var R = 6371; // Radius of the Earth in km
738
+ var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
739
+ var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
740
+ var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
741
+ Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
742
+ Math.sin(dLon / 2) * Math.sin(dLon / 2);
743
+ var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
744
+ var d = R * c;
745
+ return d;
746
+ };
747
+
748
+
749
+ /**
750
+ * Add a marker to a cluster, or creates a new cluster.
751
+ *
752
+ * @param {google.maps.Marker} marker The marker to add.
753
+ * @private
754
+ */
755
+ MarkerClusterer.prototype.addToClosestCluster_ = function(marker) {
756
+ var distance = 40000; // Some large number
757
+ var clusterToAddTo = null;
758
+ var pos = marker.getPosition();
759
+ for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {
760
+ var center = cluster.getCenter();
761
+ if (center) {
762
+ var d = this.distanceBetweenPoints_(center, marker.getPosition());
763
+ if (d < distance) {
764
+ distance = d;
765
+ clusterToAddTo = cluster;
766
+ }
767
+ }
768
+ }
769
+
770
+ if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
771
+ clusterToAddTo.addMarker(marker);
772
+ } else {
773
+ var cluster = new Cluster(this);
774
+ cluster.addMarker(marker);
775
+ this.clusters_.push(cluster);
776
+ }
777
+ };
778
+
779
+
780
+ /**
781
+ * Creates the clusters.
782
+ *
783
+ * @private
784
+ */
785
+ MarkerClusterer.prototype.createClusters_ = function() {
786
+ if (!this.ready_) {
787
+ return;
788
+ }
789
+
790
+ // Get our current map view bounds.
791
+ // Create a new bounds object so we don't affect the map.
792
+ var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),
793
+ this.map_.getBounds().getNorthEast());
794
+ var bounds = this.getExtendedBounds(mapBounds);
795
+
796
+ for (var i = 0, marker; marker = this.markers_[i]; i++) {
797
+ if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
798
+ this.addToClosestCluster_(marker);
799
+ }
800
+ }
801
+ };
802
+
803
+
804
+ /**
805
+ * A cluster that contains markers.
806
+ *
807
+ * @param {MarkerClusterer} markerClusterer The markerclusterer that this
808
+ * cluster is associated with.
809
+ * @constructor
810
+ * @ignore
811
+ */
812
+ function Cluster(markerClusterer) {
813
+ this.markerClusterer_ = markerClusterer;
814
+ this.map_ = markerClusterer.getMap();
815
+ this.gridSize_ = markerClusterer.getGridSize();
816
+ this.minClusterSize_ = markerClusterer.getMinClusterSize();
817
+ this.averageCenter_ = markerClusterer.isAverageCenter();
818
+ this.center_ = null;
819
+ this.markers_ = [];
820
+ this.bounds_ = null;
821
+ this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(),
822
+ markerClusterer.getGridSize());
823
+ }
824
+
825
+ /**
826
+ * Determins if a marker is already added to the cluster.
827
+ *
828
+ * @param {google.maps.Marker} marker The marker to check.
829
+ * @return {boolean} True if the marker is already added.
830
+ */
831
+ Cluster.prototype.isMarkerAlreadyAdded = function(marker) {
832
+ if (this.markers_.indexOf) {
833
+ return this.markers_.indexOf(marker) != -1;
834
+ } else {
835
+ for (var i = 0, m; m = this.markers_[i]; i++) {
836
+ if (m == marker) {
837
+ return true;
838
+ }
839
+ }
840
+ }
841
+ return false;
842
+ };
843
+
844
+
845
+ /**
846
+ * Add a marker the cluster.
847
+ *
848
+ * @param {google.maps.Marker} marker The marker to add.
849
+ * @return {boolean} True if the marker was added.
850
+ */
851
+ Cluster.prototype.addMarker = function(marker) {
852
+ if (this.isMarkerAlreadyAdded(marker)) {
853
+ return false;
854
+ }
855
+
856
+ if (!this.center_) {
857
+ this.center_ = marker.getPosition();
858
+ this.calculateBounds_();
859
+ } else {
860
+ if (this.averageCenter_) {
861
+ var l = this.markers_.length + 1;
862
+ var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l;
863
+ var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l;
864
+ this.center_ = new google.maps.LatLng(lat, lng);
865
+ this.calculateBounds_();
866
+ }
867
+ }
868
+
869
+ marker.isAdded = true;
870
+ this.markers_.push(marker);
871
+
872
+ var len = this.markers_.length;
873
+ if (len < this.minClusterSize_ && marker.getMap() != this.map_) {
874
+ // Min cluster size not reached so show the marker.
875
+ marker.setMap(this.map_);
876
+ }
877
+
878
+ if (len == this.minClusterSize_) {
879
+ // Hide the markers that were showing.
880
+ for (var i = 0; i < len; i++) {
881
+ this.markers_[i].setMap(null);
882
+ }
883
+ }
884
+
885
+ if (len >= this.minClusterSize_) {
886
+ marker.setMap(null);
887
+ }
888
+
889
+ this.updateIcon();
890
+ return true;
891
+ };
892
+
893
+
894
+ /**
895
+ * Returns the marker clusterer that the cluster is associated with.
896
+ *
897
+ * @return {MarkerClusterer} The associated marker clusterer.
898
+ */
899
+ Cluster.prototype.getMarkerClusterer = function() {
900
+ return this.markerClusterer_;
901
+ };
902
+
903
+
904
+ /**
905
+ * Returns the bounds of the cluster.
906
+ *
907
+ * @return {google.maps.LatLngBounds} the cluster bounds.
908
+ */
909
+ Cluster.prototype.getBounds = function() {
910
+ var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
911
+ var markers = this.getMarkers();
912
+ for (var i = 0, marker; marker = markers[i]; i++) {
913
+ bounds.extend(marker.getPosition());
914
+ }
915
+ return bounds;
916
+ };
917
+
918
+
919
+ /**
920
+ * Removes the cluster
921
+ */
922
+ Cluster.prototype.remove = function() {
923
+ this.clusterIcon_.remove();
924
+ this.markers_.length = 0;
925
+ delete this.markers_;
926
+ };
927
+
928
+
929
+ /**
930
+ * Returns the center of the cluster.
931
+ *
932
+ * @return {number} The cluster center.
933
+ */
934
+ Cluster.prototype.getSize = function() {
935
+ return this.markers_.length;
936
+ };
937
+
938
+
939
+ /**
940
+ * Returns the center of the cluster.
941
+ *
942
+ * @return {Array.<google.maps.Marker>} The cluster center.
943
+ */
944
+ Cluster.prototype.getMarkers = function() {
945
+ return this.markers_;
946
+ };
947
+
948
+
949
+ /**
950
+ * Returns the center of the cluster.
951
+ *
952
+ * @return {google.maps.LatLng} The cluster center.
953
+ */
954
+ Cluster.prototype.getCenter = function() {
955
+ return this.center_;
956
+ };
957
+
958
+
959
+ /**
960
+ * Calculated the extended bounds of the cluster with the grid.
961
+ *
962
+ * @private
963
+ */
964
+ Cluster.prototype.calculateBounds_ = function() {
965
+ var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
966
+ this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
967
+ };
968
+
969
+
970
+ /**
971
+ * Determines if a marker lies in the clusters bounds.
972
+ *
973
+ * @param {google.maps.Marker} marker The marker to check.
974
+ * @return {boolean} True if the marker lies in the bounds.
975
+ */
976
+ Cluster.prototype.isMarkerInClusterBounds = function(marker) {
977
+ return this.bounds_.contains(marker.getPosition());
978
+ };
979
+
980
+
981
+ /**
982
+ * Returns the map that the cluster is associated with.
983
+ *
984
+ * @return {google.maps.Map} The map.
985
+ */
986
+ Cluster.prototype.getMap = function() {
987
+ return this.map_;
988
+ };
989
+
990
+
991
+ /**
992
+ * Updates the cluster icon
993
+ */
994
+ Cluster.prototype.updateIcon = function() {
995
+ var zoom = this.map_.getZoom();
996
+ var mz = this.markerClusterer_.getMaxZoom();
997
+
998
+ if (mz && zoom > mz) {
999
+ // The zoom is greater than our max zoom so show all the markers in cluster.
1000
+ for (var i = 0, marker; marker = this.markers_[i]; i++) {
1001
+ marker.setMap(this.map_);
1002
+ }
1003
+ return;
1004
+ }
1005
+
1006
+ if (this.markers_.length < this.minClusterSize_) {
1007
+ // Min cluster size not yet reached.
1008
+ this.clusterIcon_.hide();
1009
+ return;
1010
+ }
1011
+
1012
+ var numStyles = this.markerClusterer_.getStyles().length;
1013
+ var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
1014
+ this.clusterIcon_.setCenter(this.center_);
1015
+ this.clusterIcon_.setSums(sums);
1016
+ this.clusterIcon_.show();
1017
+ };
1018
+
1019
+
1020
+ /**
1021
+ * A cluster icon
1022
+ *
1023
+ * @param {Cluster} cluster The cluster to be associated with.
1024
+ * @param {Object} styles An object that has style properties:
1025
+ * 'url': (string) The image url.
1026
+ * 'height': (number) The image height.
1027
+ * 'width': (number) The image width.
1028
+ * 'anchor': (Array) The anchor position of the label text.
1029
+ * 'textColor': (string) The text color.
1030
+ * 'textSize': (number) The text size.
1031
+ * 'backgroundPosition: (string) The background postition x, y.
1032
+ * @param {number=} opt_padding Optional padding to apply to the cluster icon.
1033
+ * @constructor
1034
+ * @extends google.maps.OverlayView
1035
+ * @ignore
1036
+ */
1037
+ function ClusterIcon(cluster, styles, opt_padding) {
1038
+ cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
1039
+
1040
+ this.styles_ = styles;
1041
+ this.padding_ = opt_padding || 0;
1042
+ this.cluster_ = cluster;
1043
+ this.center_ = null;
1044
+ this.map_ = cluster.getMap();
1045
+ this.div_ = null;
1046
+ this.sums_ = null;
1047
+ this.visible_ = false;
1048
+
1049
+ this.setMap(this.map_);
1050
+ }
1051
+
1052
+
1053
+ /**
1054
+ * Triggers the clusterclick event and zoom's if the option is set.
1055
+ */
1056
+ ClusterIcon.prototype.triggerClusterClick = function() {
1057
+ var markerClusterer = this.cluster_.getMarkerClusterer();
1058
+
1059
+ // Trigger the clusterclick event.
1060
+ google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_);
1061
+
1062
+ if (markerClusterer.isZoomOnClick()) {
1063
+ // Zoom into the cluster.
1064
+ this.map_.fitBounds(this.cluster_.getBounds());
1065
+ }
1066
+ };
1067
+
1068
+
1069
+ /**
1070
+ * Adding the cluster icon to the dom.
1071
+ * @ignore
1072
+ */
1073
+ ClusterIcon.prototype.onAdd = function() {
1074
+ this.div_ = document.createElement('DIV');
1075
+ if (this.visible_) {
1076
+ var pos = this.getPosFromLatLng_(this.center_);
1077
+ this.div_.style.cssText = this.createCss(pos);
1078
+ this.div_.innerHTML = this.sums_.text;
1079
+ }
1080
+
1081
+ var panes = this.getPanes();
1082
+ panes.overlayMouseTarget.appendChild(this.div_);
1083
+
1084
+ var that = this;
1085
+ google.maps.event.addDomListener(this.div_, 'click', function() {
1086
+ that.triggerClusterClick();
1087
+ });
1088
+ };
1089
+
1090
+
1091
+ /**
1092
+ * Returns the position to place the div dending on the latlng.
1093
+ *
1094
+ * @param {google.maps.LatLng} latlng The position in latlng.
1095
+ * @return {google.maps.Point} The position in pixels.
1096
+ * @private
1097
+ */
1098
+ ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) {
1099
+ var pos = this.getProjection().fromLatLngToDivPixel(latlng);
1100
+ pos.x -= parseInt(this.width_ / 2, 10);
1101
+ pos.y -= parseInt(this.height_ / 2, 10);
1102
+ return pos;
1103
+ };
1104
+
1105
+
1106
+ /**
1107
+ * Draw the icon.
1108
+ * @ignore
1109
+ */
1110
+ ClusterIcon.prototype.draw = function() {
1111
+ if (this.visible_) {
1112
+ var pos = this.getPosFromLatLng_(this.center_);
1113
+ this.div_.style.top = pos.y + 'px';
1114
+ this.div_.style.left = pos.x + 'px';
1115
+ }
1116
+ };
1117
+
1118
+
1119
+ /**
1120
+ * Hide the icon.
1121
+ */
1122
+ ClusterIcon.prototype.hide = function() {
1123
+ if (this.div_) {
1124
+ this.div_.style.display = 'none';
1125
+ }
1126
+ this.visible_ = false;
1127
+ };
1128
+
1129
+
1130
+ /**
1131
+ * Position and show the icon.
1132
+ */
1133
+ ClusterIcon.prototype.show = function() {
1134
+ if (this.div_) {
1135
+ var pos = this.getPosFromLatLng_(this.center_);
1136
+ this.div_.style.cssText = this.createCss(pos);
1137
+ this.div_.style.display = '';
1138
+ }
1139
+ this.visible_ = true;
1140
+ };
1141
+
1142
+
1143
+ /**
1144
+ * Remove the icon from the map
1145
+ */
1146
+ ClusterIcon.prototype.remove = function() {
1147
+ this.setMap(null);
1148
+ };
1149
+
1150
+
1151
+ /**
1152
+ * Implementation of the onRemove interface.
1153
+ * @ignore
1154
+ */
1155
+ ClusterIcon.prototype.onRemove = function() {
1156
+ if (this.div_ && this.div_.parentNode) {
1157
+ this.hide();
1158
+ this.div_.parentNode.removeChild(this.div_);
1159
+ this.div_ = null;
1160
+ }
1161
+ };
1162
+
1163
+
1164
+ /**
1165
+ * Set the sums of the icon.
1166
+ *
1167
+ * @param {Object} sums The sums containing:
1168
+ * 'text': (string) The text to display in the icon.
1169
+ * 'index': (number) The style index of the icon.
1170
+ */
1171
+ ClusterIcon.prototype.setSums = function(sums) {
1172
+ this.sums_ = sums;
1173
+ this.text_ = sums.text;
1174
+ this.index_ = sums.index;
1175
+ if (this.div_) {
1176
+ this.div_.innerHTML = sums.text;
1177
+ }
1178
+
1179
+ this.useStyle();
1180
+ };
1181
+
1182
+
1183
+ /**
1184
+ * Sets the icon to the the styles.
1185
+ */
1186
+ ClusterIcon.prototype.useStyle = function() {
1187
+ var index = Math.max(0, this.sums_.index - 1);
1188
+ index = Math.min(this.styles_.length - 1, index);
1189
+ var style = this.styles_[index];
1190
+ this.url_ = style['url'];
1191
+ this.height_ = style['height'];
1192
+ this.width_ = style['width'];
1193
+ this.textColor_ = style['textColor'];
1194
+ this.anchor_ = style['anchor'];
1195
+ this.textSize_ = style['textSize'];
1196
+ this.backgroundPosition_ = style['backgroundPosition'];
1197
+ };
1198
+
1199
+
1200
+ /**
1201
+ * Sets the center of the icon.
1202
+ *
1203
+ * @param {google.maps.LatLng} center The latlng to set as the center.
1204
+ */
1205
+ ClusterIcon.prototype.setCenter = function(center) {
1206
+ this.center_ = center;
1207
+ };
1208
+
1209
+
1210
+ /**
1211
+ * Create the css text based on the position of the icon.
1212
+ *
1213
+ * @param {google.maps.Point} pos The position.
1214
+ * @return {string} The css style text.
1215
+ */
1216
+ ClusterIcon.prototype.createCss = function(pos) {
1217
+ var style = [];
1218
+ style.push('background-image:url(' + this.url_ + ');');
1219
+ var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0';
1220
+ style.push('background-position:' + backgroundPosition + ';');
1221
+
1222
+ if (typeof this.anchor_ === 'object') {
1223
+ if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 &&
1224
+ this.anchor_[0] < this.height_) {
1225
+ style.push('height:' + (this.height_ - this.anchor_[0]) +
1226
+ 'px; padding-top:' + this.anchor_[0] + 'px;');
1227
+ } else {
1228
+ style.push('height:' + this.height_ + 'px; line-height:' + this.height_ +
1229
+ 'px;');
1230
+ }
1231
+ if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 &&
1232
+ this.anchor_[1] < this.width_) {
1233
+ style.push('width:' + (this.width_ - this.anchor_[1]) +
1234
+ 'px; padding-left:' + this.anchor_[1] + 'px;');
1235
+ } else {
1236
+ style.push('width:' + this.width_ + 'px; text-align:center;');
1237
+ }
1238
+ } else {
1239
+ style.push('height:' + this.height_ + 'px; line-height:' +
1240
+ this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;');
1241
+ }
1242
+
1243
+ var txtColor = this.textColor_ ? this.textColor_ : 'black';
1244
+ var txtSize = this.textSize_ ? this.textSize_ : 11;
1245
+
1246
+ style.push('cursor:pointer; top:' + pos.y + 'px; left:' +
1247
+ pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' +
1248
+ txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
1249
+ return style.join('');
1250
+ };
1251
+
1252
+
1253
+ // Export Symbols for Closure
1254
+ // If you are not going to compile with closure then you can remove the
1255
+ // code below.
1256
+ window['MarkerClusterer'] = MarkerClusterer;
1257
+ MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker;
1258
+ MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers;
1259
+ MarkerClusterer.prototype['clearMarkers'] =
1260
+ MarkerClusterer.prototype.clearMarkers;
1261
+ MarkerClusterer.prototype['fitMapToMarkers'] =
1262
+ MarkerClusterer.prototype.fitMapToMarkers;
1263
+ MarkerClusterer.prototype['getCalculator'] =
1264
+ MarkerClusterer.prototype.getCalculator;
1265
+ MarkerClusterer.prototype['getGridSize'] =
1266
+ MarkerClusterer.prototype.getGridSize;
1267
+ MarkerClusterer.prototype['getExtendedBounds'] =
1268
+ MarkerClusterer.prototype.getExtendedBounds;
1269
+ MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap;
1270
+ MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers;
1271
+ MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom;
1272
+ MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles;
1273
+ MarkerClusterer.prototype['getTotalClusters'] =
1274
+ MarkerClusterer.prototype.getTotalClusters;
1275
+ MarkerClusterer.prototype['getTotalMarkers'] =
1276
+ MarkerClusterer.prototype.getTotalMarkers;
1277
+ MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw;
1278
+ MarkerClusterer.prototype['removeMarker'] =
1279
+ MarkerClusterer.prototype.removeMarker;
1280
+ MarkerClusterer.prototype['removeMarkers'] =
1281
+ MarkerClusterer.prototype.removeMarkers;
1282
+ MarkerClusterer.prototype['resetViewport'] =
1283
+ MarkerClusterer.prototype.resetViewport;
1284
+ MarkerClusterer.prototype['repaint'] =
1285
+ MarkerClusterer.prototype.repaint;
1286
+ MarkerClusterer.prototype['setCalculator'] =
1287
+ MarkerClusterer.prototype.setCalculator;
1288
+ MarkerClusterer.prototype['setGridSize'] =
1289
+ MarkerClusterer.prototype.setGridSize;
1290
+ MarkerClusterer.prototype['setMaxZoom'] =
1291
+ MarkerClusterer.prototype.setMaxZoom;
1292
+ MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd;
1293
+ MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw;
1294
+
1295
+ Cluster.prototype['getCenter'] = Cluster.prototype.getCenter;
1296
+ Cluster.prototype['getSize'] = Cluster.prototype.getSize;
1297
+ Cluster.prototype['getMarkers'] = Cluster.prototype.getMarkers;
1298
+
1299
+ ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd;
1300
+ ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw;
1301
+ ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove;
1302
+
1303
+ Object.keys = Object.keys || function(o) {
1304
+ var result = [];
1305
+ for(var name in o) {
1306
+ if (o.hasOwnProperty(name))
1307
+ result.push(name);
1308
+ }
1309
+ return result;
1310
+ };
js/indabox/storepickup.js ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var IndaboxStorePickup = Class.create();
2
+ IndaboxStorePickup.prototype = {
3
+
4
+ initialize: function(changeMethodUrl, searchUrl, locationUrl, mediaUrl) {
5
+ this.changeMethodUrl = changeMethodUrl;
6
+ this.searchUrl = searchUrl;
7
+ this.locationUrl = locationUrl;
8
+ this.mediaUrl = mediaUrl;
9
+
10
+ this.points = [];
11
+ this.markers = [];
12
+ this.location = null;
13
+ this.map = null;
14
+ this.clusterer = null;
15
+ this.infoWindow = null;
16
+
17
+ this.onInit = null;
18
+ this.onSearchStart = null;
19
+ this.onSearchEnd = null;
20
+ this.onSelectPoint = null;
21
+
22
+ this.setUpHook();
23
+ },
24
+
25
+ setUpHook: function () {
26
+ var fallbackValidate = ShippingMethod.prototype.validate,
27
+ fallbackNextStep = ShippingMethod.prototype.nextStep;
28
+ ShippingMethod.prototype.validate = function () {
29
+ var result,
30
+ pointId,
31
+ methods;
32
+
33
+ result = fallbackValidate.call(this);
34
+ if ( ! result)
35
+ return false;
36
+
37
+ methods = document.getElementsByName('shipping_method');
38
+ for (var i=0; i<methods.length; i++) {
39
+ if (methods[i].checked && methods[i].value !== 'ibstorepickup_ibstorepickup') {
40
+ return true;
41
+ }
42
+ }
43
+
44
+ if ( ! $('indabox_accept_terms').checked) {
45
+ alert(Translator.translate('You should accept IndaBox terms and conditions').stripTags());
46
+ return false;
47
+ }
48
+ pointId = parseInt($('indabox_point_id').value, 10);
49
+ if (pointId <= 0) {
50
+ alert(Translator.translate('You should select one of available pick-up points to continue').stripTags());
51
+ return false;
52
+ }
53
+
54
+ return true;
55
+ };
56
+
57
+ ShippingMethod.prototype.nextStep = function (transport) {
58
+ fallbackNextStep.call(this, transport);
59
+ checkout.reloadProgressBlock('shipping');
60
+ };
61
+ },
62
+
63
+ rebound: function () {
64
+ var i,
65
+ bounds;
66
+
67
+ if ( ! this.markers.length)
68
+ return;
69
+
70
+ bounds = new google.maps.LatLngBounds();
71
+
72
+ if (this.location)
73
+ bounds.extend(this.location.getPosition());
74
+
75
+ for (i = 0; i < this.markers.length; i++)
76
+ bounds.extend(this.markers[i].getPosition());
77
+
78
+ this.map.fitBounds(bounds);
79
+ },
80
+
81
+ showPointInfo: function (marker, idx) {
82
+ var point = this.points[idx];
83
+ this.infoWindow.setContent(
84
+ '<div class="indabox-info-window">' +
85
+ '<div class="row title">' + point.name + '</div>' +
86
+ '<div class="row"></div>' +
87
+ '<div class="row distance"><strong>' + Translator.translate('Distance') + ': </strong>' + point.distance + ' km </div>' +
88
+ '<div class="row telephone"><strong>' + Translator.translate('Telephone') + ': </strong>' + point.telephone + '</div>'+
89
+ '<div class="row select"><a class="indabox-select-me" rel="' + idx + '" href="#">' + Translator.translate('Select this pick-up point') + '</a></div>'
90
+ );
91
+ this.infoWindow.open(this.map, marker);
92
+ },
93
+
94
+ setPoints: function (points) {
95
+ var i, marker, point, caller = this;
96
+ for (i = 0; i < this.markers.length; i++)
97
+ this.markers[i].setMap(null);
98
+
99
+ this.clusterer.clearMarkers();
100
+ this.markers.length = 0;
101
+
102
+ this.points = points;
103
+ if ( ! this.points.length)
104
+ return;
105
+
106
+ for (i = 0; i < this.points.length; i++) {
107
+ point = this.points[i];
108
+ marker = new google.maps.Marker({
109
+ position: new google.maps.LatLng(point.latitude, point.longitude),
110
+ map: this.map,
111
+ icon: this.mediaUrl + 'marker_point.png',
112
+ shadow: 'https://chart.googleapis.com/chart?chst=d_map_pin_shadow'
113
+ });
114
+ google.maps.event.addListener(marker, 'click', (function(marker, i) {
115
+ return function () {
116
+ caller.showPointInfo(marker, i);
117
+ }
118
+ })(marker, i));
119
+ this.markers.push(marker);
120
+ }
121
+
122
+ this.clusterer.addMarkers(this.markers);
123
+
124
+ this.rebound();
125
+ },
126
+
127
+ choosePoint: function (idx) {
128
+ this.infoWindow.close();
129
+ if (typeof(this.onSelectPoint) === 'function')
130
+ this.onSelectPoint.call(this, this.points[idx]);
131
+ },
132
+
133
+ initMap: function (container) {
134
+ var mapOptions = {
135
+ zoom: 5,
136
+ center: new google.maps.LatLng(41.9000, 12.4833)
137
+ },
138
+ caller = this;
139
+
140
+ Event.observe(container, 'click', function (event) {
141
+ var target = Event.findElement(event);
142
+
143
+ if (target.hasClassName('indabox-select-me')) {
144
+ Event.stop(event);
145
+ caller.choosePoint(parseInt(target.rel), 10);
146
+ }
147
+ });
148
+
149
+ this.infoWindow = new google.maps.InfoWindow();
150
+ this.map = new google.maps.Map(document.getElementById(container), mapOptions);
151
+ this.clusterer = new MarkerClusterer(this.map, this.markers);
152
+
153
+ if (typeof(this.onInit) === 'function')
154
+ this.onInit.call(this);
155
+ },
156
+
157
+ setLocation: function (lat, lng) {
158
+ this.location = new google.maps.Marker({
159
+ position: new google.maps.LatLng(lat, lng),
160
+ map: this.map,
161
+ icon: this.mediaUrl + 'marker_location.png',
162
+ shadow: 'https://chart.googleapis.com/chart?chst=d_map_pin_shadow'
163
+ });
164
+
165
+ this.rebound();
166
+ },
167
+
168
+ locate: function (address) {
169
+ var url = this.locationUrl,
170
+ caller = this;
171
+
172
+ if (this.location) {
173
+ this.location.setMap(null);
174
+ this.location = null;
175
+ }
176
+
177
+ url = url + 'address/' + address;
178
+
179
+ new Ajax.Request(url, {
180
+ onSuccess: function(transport) {
181
+ var json = transport.responseText.evalJSON();
182
+
183
+ if (json.error) {
184
+ return;
185
+ }
186
+
187
+ caller.setLocation(json.latitude, json.longitude);
188
+ }
189
+ });
190
+ },
191
+
192
+ search: function (address, radius) {
193
+ var url = this.searchUrl,
194
+ caller = this;
195
+
196
+ if (typeof(this.onSearchStart) === 'function')
197
+ this.onSearchStart.call(this);
198
+
199
+ this.locate(address);
200
+
201
+ url = url + 'address/' + address + '/radius/' + radius;
202
+
203
+ new Ajax.Request(url, {
204
+ onComplete: function () {
205
+ if (typeof(caller.onSearchEnd) === 'function')
206
+ caller.onSearchEnd.call(caller, caller.points);
207
+ },
208
+ onSuccess: function(transport) {
209
+ var json = transport.responseText.evalJSON();
210
+
211
+ if (json.error) {
212
+ alert(json.message ? json.message : Translator.translate('Error'));
213
+ return;
214
+ }
215
+
216
+ caller.setPoints(json.points);
217
+ }
218
+ });
219
+ },
220
+
221
+ initGoogleMap: function (scriptUrl, container) {
222
+ if ( ! window.google || ! google.maps)
223
+ this.loadGoogleMap(scriptUrl, container);
224
+ else
225
+ this.initMap(container);
226
+ },
227
+
228
+ loadGoogleMap: function (scriptUrl, container) {
229
+ var script = document.createElement('script'),
230
+ functionName = 'initMap' + Math.floor(Math.random() * 1000001),
231
+ caller = this;
232
+
233
+ window[functionName] = function () {
234
+ caller.initMap(container);
235
+ }
236
+
237
+ script.type = 'text/javascript';
238
+ script.src = scriptUrl + '&callback=' + functionName;
239
+ document.body.appendChild(script);
240
+ },
241
+
242
+ setUseStorePickup: function(flag)
243
+ {
244
+ var url = this.changeMethodUrl;
245
+
246
+ if (flag)
247
+ url += 'flag/1';
248
+ else
249
+ url += 'flag/0';
250
+
251
+ var request = new Ajax.Request(url, {method: 'get', onFailure: ""});
252
+ }
253
+
254
+ }
media/indabox/marker_location.png ADDED
Binary file
media/indabox/marker_point.png ADDED
Binary file
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>indabox</name>
4
+ <version>1.0.1.1</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>IndaBox Shipping Method Extension</summary>
10
+ <description>Allow to use shipping with IndaBox</description>
11
+ <notes>First stable release</notes>
12
+ <authors><author><name>IndaBox</name><user>indabox</user><email>info@indabox.it</email></author><author><name>Advanced Logic</name><user>alogic</user><email>advancedlogiceu@gmail.com</email></author></authors>
13
+ <date>2014-06-11</date>
14
+ <time>12:16:35</time>
15
+ <contents><target name="magelocal"><dir name="IndaBox"><dir name="StorePickup"><dir name="Block"><dir name="Adminhtml"><dir name="Configuration"><file name="Manual.php" hash="1ea9346d7e7d04bb4dc4f4f8dfb9be82"/></dir></dir><dir name="Checkout"><dir name="Onepage"><dir name="Payment"><file name="Methods.php" hash="340a0a40207e30252a4fc08671be8a46"/></dir><dir name="Shipping"><dir name="Method"><file name="Available.php" hash="967c98f5c0bcd57ca8c793e1f8de2498"/></dir></dir></dir></dir><file name="Map.php" hash="2c644a416a2ffa7b23a19ed6ef701690"/></dir><file name="Exception.php" hash="a7e958b8282d8bd49d658edfad5df19e"/><dir name="Helper"><file name="Config.php" hash="31dba4d9d880082528218bbcd843db2c"/><file name="Data.php" hash="40a1bb55faa368ea74ac3b8cbf5a430f"/></dir><dir name="Model"><file name="Api.php" hash="585755e2c2658c18f5192ad619a339d4"/><dir name="Carrier"><file name="Storepickup.php" hash="04c74ad0e3390b6f1f5d7d3f66cb4b98"/></dir><file name="GoogleMaps.php" hash="c2eb3bceb545b0e10b689f7de0866778"/><file name="Observer.php" hash="68789fa99acefc675193032da3f720bd"/><dir name="Order"><file name="Point.php" hash="657eda40931dd6feb86b974bb36f5d96"/></dir><file name="Point.php" hash="cedd47800610db090a8937cbe8066571"/><file name="Points.php" hash="784d98055d815e48f20a4c302304d817"/><dir name="Resource"><dir name="Order"><dir name="Point"><file name="Collection.php" hash="7b4e48ec040ed1f4e44abe5a9a964daa"/></dir><file name="Point.php" hash="31a9898dbbe274afe18d6bf30f5eeb9e"/></dir></dir><dir name="Source"><file name="Payment.php" hash="f223a5951a18f2b293656fb696d4ac57"/><file name="Selectorpayment.php" hash="82f18db0f8be393a01d234d4f9c4fbc4"/></dir></dir><dir name="controllers"><file name="IndexController.php" hash="8891642df2404ab74d5804a8a74ffe5d"/></dir><dir name="etc"><file name="config.xml" hash="af7f36e74ec99ce89efd810141c04df2"/><file name="jstranslator.xml" hash="8ec2ada62b1484f3c6141006a3c4d184"/><file name="system.xml" hash="68c44ba242835e97869be4f578885b61"/></dir><dir name="sql"><dir name="ibstorepickup_setup"><file name="install-0.1.0.php" hash="32c4475a704617e53a92dad17a91000e"/></dir></dir></dir></dir></target><target name="mage"><dir name="js"><dir name="indabox"><file name="markerclusterer.js" hash="6c3ce8c9377f8edf520c3cee2272bc29"/><file name="storepickup.js" hash="efe49a4d3b914bc8d79eb0cb33ff8680"/></dir></dir><dir name="app"><dir name="etc"><dir name="modules"><file name="IndaBox_StorePickup.xml" hash="bc600feadf12b176c134158ca2f12726"/></dir></dir></dir></target><target name="magemedia"><dir name="indabox"><file name="marker_location.png" hash="000a6513abbe2c9683b19f49873710c5"/><file name="marker_point.png" hash="ea488afc95fd4713c3ecbfcb7a0cfb38"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="template"><dir name="ibstorepickup"><dir name="checkout"><dir name="onepage"><file name="billing.phtml" hash="9e2ec2ee9e469037af8ba3d70a718d2c"/></dir></dir><file name="map.phtml" hash="d60d7afa71cdbf48a9c7c5577849e21f"/></dir></dir><dir name="layout"><file name="ibstorepickup.xml" hash="a8fd146391dc8ece400893e4034344ec"/></dir></dir></dir></dir></target><target name="mageskin"><dir name="frontend"><dir name="base"><dir name="default"><dir name="indabox"><dir><dir name="css"><file name="storepickup.css" hash="30f63f16089efea263bb247e9e8888bd"/></dir><dir name="images"><file name="opc-ajax-loader.gif" hash="e805ea7eca1f34c75ba0f93780d32d38"/></dir></dir></dir></dir></dir></dir></target><target name="magelocale"><dir name="it_IT"><file name="IndaBox_StorePickup.csv" hash="11bf579e01afd50d5207ddda8f9d56f4"/></dir><dir name="en_US"><file name="IndaBox_StorePickup.csv" hash="86bdcd9e56711b3f98461a5e1ef2e41a"/></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
18
+ </package>
skin/frontend/base/default/indabox/css/storepickup.css ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #indabox_conditions iframe {
2
+ border: 0px;
3
+ margin: 20px 0;
4
+ height: 360px;
5
+ width: 100%;
6
+ }
7
+
8
+ #indabox_search+img{
9
+ display: none;
10
+ margin: 2px 0 0 10px;
11
+ }
12
+
13
+ #indabox_map {
14
+ }
15
+
16
+ #indabox_google_map {
17
+ width: 100%;
18
+ height: 360px;
19
+ margin-top: 20px;
20
+ }
21
+
22
+ #indabox_point {
23
+ margin-top: 20px;
24
+ }
25
+
26
+ #indabox_search.in-progress+img{
27
+ display: inline-block;
28
+ }
29
+
30
+ .indabox-info-window {
31
+ padding: 5px;
32
+ font-size: 13px;
33
+ color: #555;
34
+ line-height: 18px;
35
+ font-family: arial;
36
+ min-width: 300px;
37
+ }
38
+
39
+ .indabox-info-window .row {
40
+ margin: 5px 0;
41
+ }
42
+
43
+ .indabox-info-window .row.title {
44
+ color: #457085;
45
+ text-transform: capitalize;
46
+ font-size: 16px;
47
+ margin-bottom: 15px;
48
+ }
49
+
50
+ .indabox-info-window .row strong {
51
+ color: grey !important;
52
+ font-weight: 400 !important;
53
+ }
54
+
55
+ .indabox-info-window .row a {
56
+ margin-top: 7px;
57
+ float: left;
58
+ color: #9bae1d;
59
+ font-size: 14px;
60
+ text-decoration: none;
61
+ }
skin/frontend/base/default/indabox/images/opc-ajax-loader.gif ADDED
Binary file