Gene_Doddle - Version 1.0.0

Version Notes

Doddle will give your customers a parcel pick up and drop off service that fits with their busy lives.

Download this release

Release Info

Developer Dave Macaulay
Extension Gene_Doddle
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

Files changed (47) hide show
  1. app/code/community/Gene/Doddle/Block/Adminhtml/Sales/Order/View/Shippingmethod.php +39 -0
  2. app/code/community/Gene/Doddle/Block/Adminhtml/System/Config/Doddle/Moduleversion.php +95 -0
  3. app/code/community/Gene/Doddle/Block/Onepage/Billing/Option.php +49 -0
  4. app/code/community/Gene/Doddle/Block/Onepage/Setup.php +31 -0
  5. app/code/community/Gene/Doddle/Block/Onepage/Shipping/Method/Doddle.php +102 -0
  6. app/code/community/Gene/Doddle/Block/Onepage/Shipping/Method/Doddle/Table.php +47 -0
  7. app/code/community/Gene/Doddle/Helper/Data.php +20 -0
  8. app/code/community/Gene/Doddle/Model/Api/Doddle/Abstract.php +237 -0
  9. app/code/community/Gene/Doddle/Model/Api/Doddle/Preadvice.php +192 -0
  10. app/code/community/Gene/Doddle/Model/Api/Doddle/Stores.php +200 -0
  11. app/code/community/Gene/Doddle/Model/Carrier.php +81 -0
  12. app/code/community/Gene/Doddle/Model/Observer.php +482 -0
  13. app/code/community/Gene/Doddle/Model/Request.php +103 -0
  14. app/code/community/Gene/Doddle/Model/Resource/Request.php +16 -0
  15. app/code/community/Gene/Doddle/Model/Resource/Request/Collection.php +16 -0
  16. app/code/community/Gene/Doddle/Model/Store.php +187 -0
  17. app/code/community/Gene/Doddle/Model/System/Config/Environment.php +26 -0
  18. app/code/community/Gene/Doddle/controllers/IndexController.php +52 -0
  19. app/code/community/Gene/Doddle/etc/config.xml +206 -0
  20. app/code/community/Gene/Doddle/etc/system.xml +301 -0
  21. app/code/community/Gene/Doddle/sql/gene_doddle_setup/install-0.1.0.php +49 -0
  22. app/design/adminhtml/default/default/template/gene/doddle/shippingmethod.phtml +34 -0
  23. app/design/frontend/base/default/layout/gene/doddle.xml +28 -0
  24. app/design/frontend/base/default/template/gene/doddle/onepage/billing/option.phtml +11 -0
  25. app/design/frontend/base/default/template/gene/doddle/onepage/progress/shipping_method.phtml +38 -0
  26. app/design/frontend/base/default/template/gene/doddle/onepage/setup.phtml +12 -0
  27. app/design/frontend/base/default/template/gene/doddle/onepage/shipping/method/doddle.phtml +115 -0
  28. app/design/frontend/base/default/template/gene/doddle/onepage/shipping/method/doddle/table.phtml +69 -0
  29. app/etc/modules/Gene_Doddle.xml +9 -0
  30. js/gene/doddle/doddle.js +349 -0
  31. package.xml +18 -0
  32. skin/frontend/base/default/css/gene/doddle/config.codekit +805 -0
  33. skin/frontend/base/default/css/gene/doddle/doddle.css +221 -0
  34. skin/frontend/base/default/css/gene/doddle/doddle.less +305 -0
  35. skin/frontend/base/default/css/gene/doddle/fonts/doddle-icon.eot +0 -0
  36. skin/frontend/base/default/css/gene/doddle/fonts/doddle-icon.svg +11 -0
  37. skin/frontend/base/default/css/gene/doddle/fonts/doddle-icon.ttf +0 -0
  38. skin/frontend/base/default/css/gene/doddle/fonts/doddle-icon.woff +0 -0
  39. skin/frontend/base/default/images/gene/doddle/logo.png +0 -0
  40. skin/frontend/base/default/images/gene/doddle/opc-ajax-loader.gif +0 -0
  41. skin/frontend/rwd/default/css/gene/doddle/config.codekit +805 -0
  42. skin/frontend/rwd/default/css/gene/doddle/doddle.css +221 -0
  43. skin/frontend/rwd/default/css/gene/doddle/doddle.less +305 -0
  44. skin/frontend/rwd/default/css/gene/doddle/fonts/doddle-icon.eot +0 -0
  45. skin/frontend/rwd/default/css/gene/doddle/fonts/doddle-icon.svg +11 -0
  46. skin/frontend/rwd/default/css/gene/doddle/fonts/doddle-icon.ttf +0 -0
  47. skin/frontend/rwd/default/css/gene/doddle/fonts/doddle-icon.woff +0 -0
app/code/community/Gene/Doddle/Block/Adminhtml/Sales/Order/View/Shippingmethod.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Block_Adminhtml_Sales_Order_View_Shippingmethod
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Block_Adminhtml_Sales_Order_View_Shippingmethod extends Mage_Adminhtml_Block_Sales_Order_View_Tab_Info
9
+ {
10
+
11
+ /**
12
+ * Internal constructor, that is called from real constructor
13
+ *
14
+ */
15
+ protected function _construct()
16
+ {
17
+ parent::_construct();
18
+
19
+ // Set our template
20
+ $this->setTemplate('gene/doddle/shippingmethod.phtml');
21
+ }
22
+
23
+ /**
24
+ * Return the pre-advice ID
25
+ *
26
+ * @return Gene_Doddle_Model_Resource_Request_Collection
27
+ */
28
+ protected function getPreAdvices()
29
+ {
30
+ $request = Mage::getResourceModel('gene_doddle/request_collection')
31
+ ->addFieldToFilter('order_id', $this->getOrder()->getId());
32
+
33
+ if($request->count()) {
34
+ return $request;
35
+ }
36
+
37
+ return false;
38
+ }
39
+ }
app/code/community/Gene/Doddle/Block/Adminhtml/System/Config/Doddle/Moduleversion.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Block_Adminhtml_System_Config_Doddle_Moduleversion
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Block_Adminhtml_System_Config_Doddle_Moduleversion
9
+ extends Mage_Adminhtml_Block_Abstract implements Varien_Data_Form_Element_Renderer_Interface
10
+ {
11
+ /**
12
+ * Render element html
13
+ *
14
+ * @param Varien_Data_Form_Element_Abstract $element
15
+ * @return string
16
+ */
17
+ public function render(Varien_Data_Form_Element_Abstract $element)
18
+ {
19
+ $useContainerId = $element->getData('use_container_id');
20
+ return sprintf('<tr id="row_%s">
21
+ <td class="label">
22
+ <strong id="%s">%s</strong>
23
+ </td>
24
+ <td class="value">
25
+ <span id="module-version">%s</span>
26
+ <div id="recent-version">
27
+ <div style="padding: 8px 0;line-height: 16px;">
28
+ <img src="' . $this->getSkinUrl('images/rule-ajax-loader.gif') . '" align="left" style="margin-right: 3px;" /> ' . $this->__('Checking latest version...') . '
29
+ </div>
30
+ </div>
31
+ </td>
32
+ </tr>',
33
+ $element->getHtmlId(), $element->getHtmlId(), $element->getLabel(), $this->getVersionHtml()
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Inform the user there version will not work
39
+ * @return string
40
+ */
41
+ private function getVersionHtml()
42
+ {
43
+ $response = Mage::getConfig()->getModuleConfig('Gene_Doddle')->version;
44
+ $response.= '
45
+ <script type="text/javascript">
46
+
47
+ // Define the module version in the window
48
+ var moduleVersion = \'' . Mage::getConfig()->getModuleConfig('Gene_Doddle')->version . '\';
49
+
50
+ // Once the dom has loaded make the checkout
51
+ document.observe("dom:loaded", function() {
52
+ try {
53
+ new Ajax.Request("//doddle.gene.co.uk", {
54
+ method: "post", parameters: {version: moduleVersion},
55
+ onCreate: function(response) {
56
+ var t = response.transport;
57
+ t.setRequestHeader = t.setRequestHeader.wrap(function(original, k, v) {
58
+ if (/^(accept|accept-language|content-language)$/i.test(k))
59
+ return original(k, v);
60
+ if (/^content-type$/i.test(k) &&
61
+ /^(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)(;.+)?$/i.test(v))
62
+ return original(k, v);
63
+ return;
64
+ });
65
+ },
66
+ onSuccess: function(transport) {
67
+ try {
68
+ json = eval(\'(\' + transport.responseText + \')\');
69
+ } catch(e){}
70
+
71
+ // Is there a message to be displayed to the user?
72
+ if(json.message) {
73
+ $(\'recent-version\').innerHTML = json.message;
74
+ }
75
+
76
+ if(json.latest == true) {
77
+ $(\'module-version\').setStyle({color: \'green\'});
78
+ } else {
79
+ $(\'module-version\').setStyle({color: \'darkred\'});
80
+ }
81
+ },
82
+ onFailure: function() {
83
+ $(\'recent-version\').innerHTML = \'<span style="color:darkred;">Unable to check for updates</span>\';
84
+ }
85
+ });
86
+ } catch(e) {
87
+ $(\'recent-version\').innerHTML = \'<span style="color:darkred;">Unable to check for updates</span>\';
88
+ }
89
+ });
90
+ </script>
91
+ ';
92
+
93
+ return $response;
94
+ }
95
+ }
app/code/community/Gene/Doddle/Block/Onepage/Billing/Option.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Block_Onepage_Shipping_Method_Doddle
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Block_Onepage_Billing_Option extends Mage_Core_Block_Template
9
+ {
10
+
11
+ /**
12
+ * Set the template on construction
13
+ *
14
+ * @return $this
15
+ */
16
+ protected function _construct()
17
+ {
18
+ // Run any parent functionality
19
+ parent::_construct();
20
+
21
+ // Force set our template
22
+ $this->setTemplate('gene/doddle/onepage/billing/option.phtml');
23
+
24
+ // Just in case anything is chaining
25
+ return $this;
26
+ }
27
+
28
+ /**
29
+ * Return the more information block
30
+ *
31
+ * @return mixed
32
+ */
33
+ protected function getMoreInformation()
34
+ {
35
+ // Check that more information is set
36
+ if($moreInformation = Mage::getStoreConfig('carriers/gene_doddle/more_information')) {
37
+
38
+ // Strip out any nasty tags
39
+ $filter = new Zend_Filter_StripTags(array(
40
+ 'allowTags' => array('a', 'p', 'br', 'hr', 'h2', 'h3', 'h4', 'strong', 'em')
41
+ ));
42
+ return $filter->filter($moreInformation);
43
+
44
+ }
45
+
46
+ return false;
47
+ }
48
+
49
+ }
app/code/community/Gene/Doddle/Block/Onepage/Setup.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Block_Onepage_Setup
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Block_Onepage_Setup extends Mage_Core_Block_Template
9
+ {
10
+
11
+ /**
12
+ * Return the value of the shipping method radio button
13
+ *
14
+ * @return string
15
+ */
16
+ protected function getDoddleShippingMethodValue()
17
+ {
18
+ return Mage::helper('gene_doddle')->getShippingMethodCode();
19
+ }
20
+
21
+ /**
22
+ * Return the configuration Google API key
23
+ *
24
+ * @return mixed
25
+ */
26
+ protected function getGoogleApiKey()
27
+ {
28
+ return Mage::getStoreConfig('carriers/gene_doddle/google_api_key');
29
+ }
30
+
31
+ }
app/code/community/Gene/Doddle/Block/Onepage/Shipping/Method/Doddle.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Block_Onepage_Shipping_Method_Doddle
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Block_Onepage_Shipping_Method_Doddle extends Mage_Core_Block_Template
9
+ {
10
+
11
+ /**
12
+ * Set the template on construction
13
+ *
14
+ * @return $this
15
+ */
16
+ protected function _construct()
17
+ {
18
+ // Run any parent functionality
19
+ parent::_construct();
20
+
21
+ // Force set our template
22
+ $this->setTemplate('gene/doddle/onepage/shipping/method/doddle.phtml');
23
+
24
+ // Just in case anything is chaining
25
+ return $this;
26
+ }
27
+
28
+ /**
29
+ * Retrieve parent block
30
+ *
31
+ * @return Mage_Checkout_Block_Onepage_Shipping_Method_Available
32
+ */
33
+ public function getParentBlock()
34
+ {
35
+ return $this->_parentBlock;
36
+ }
37
+
38
+ /**
39
+ * Check the cookie to see whether or not the shipping method step should only show Doddle
40
+ *
41
+ * @return mixed
42
+ */
43
+ protected function onlyDoddle()
44
+ {
45
+ return Mage::registry('doddle_only');
46
+ }
47
+
48
+ /**
49
+ * Return the raw price of the shipping method
50
+ *
51
+ * @return mixed
52
+ */
53
+ protected function getPrice()
54
+ {
55
+ return Mage::getModel('gene_doddle/carrier')->getConfigData('price');
56
+ }
57
+
58
+ /**
59
+ * Return the price of the shipping method formatted
60
+ *
61
+ * @return mixed
62
+ */
63
+ protected function getFormattedPrice()
64
+ {
65
+ return Mage::app()->getStore()->getBaseCurrency()->format($this->getPrice());
66
+ }
67
+
68
+ /**
69
+ * Return the more information block
70
+ *
71
+ * @return mixed
72
+ */
73
+ protected function getMoreInformation()
74
+ {
75
+ // Check that more information is set
76
+ if($moreInformation = Mage::getStoreConfig('carriers/gene_doddle/more_information')) {
77
+
78
+ // Strip out any nasty tags
79
+ $filter = new Zend_Filter_StripTags(array(
80
+ 'allowTags' => array('a', 'p', 'br', 'hr', 'h2', 'h3', 'h4', 'strong', 'em')
81
+ ));
82
+ return $filter->filter($moreInformation);
83
+
84
+ }
85
+
86
+ return false;
87
+ }
88
+
89
+ /**
90
+ * Only render this block if the Doddle module is enabled and available for this quote
91
+ *
92
+ * @return string
93
+ */
94
+ protected function _toHtml()
95
+ {
96
+ if(in_array(Mage::getSingleton('gene_doddle/carrier')->getCarrierCode(), array_keys($this->getParentBlock()->getShippingRates()))) {
97
+ return parent::_toHtml();
98
+ }
99
+ return '';
100
+ }
101
+
102
+ }
app/code/community/Gene/Doddle/Block/Onepage/Shipping/Method/Doddle/Table.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Block_Onepage_Shipping_Method_Doddle_Table
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Block_Onepage_Shipping_Method_Doddle_Table extends Mage_Core_Block_Template
9
+ {
10
+ /**
11
+ * Return the store API
12
+ *
13
+ * @return \Gene_Doddle_Model_Api_Doddle_Stores
14
+ */
15
+ private function _getStoreApi()
16
+ {
17
+ return Mage::getSingleton('gene_doddle/api_doddle_stores');
18
+ }
19
+
20
+ /**
21
+ * Return the stores
22
+ *
23
+ * @return array
24
+ * @throws \Exception
25
+ */
26
+ protected function getStores()
27
+ {
28
+ return $this->_getStoreApi()->getClosestStores($this->getRequest()->getParam('lat'), $this->getRequest()->getParam('long'));
29
+ }
30
+
31
+ /**
32
+ * Return the collection date
33
+ *
34
+ * @return string
35
+ */
36
+ protected function getCollectionDate()
37
+ {
38
+ // Attempt to load the configuration option
39
+ $daysUntilCollection = Mage::getStoreConfig('carriers/gene_doddle/collection_lead_time');
40
+ if($daysUntilCollection) {
41
+ return Mage::getModel('core/date')->date('l, d F', strtotime('+' . $daysUntilCollection.' days'));
42
+ }
43
+
44
+ return Mage::helper('gene_doddle')->__('<em>Unknown</em>');
45
+ }
46
+
47
+ }
app/code/community/Gene/Doddle/Helper/Data.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Helper_Data
5
+ * @author Dave Macaulay <dave@gene.co.uk>
6
+ */
7
+ class Gene_Doddle_Helper_Data extends Mage_Core_Helper_Abstract
8
+ {
9
+
10
+ /**
11
+ * Return the full shipping method code
12
+ *
13
+ * @return string
14
+ */
15
+ public function getShippingMethodCode()
16
+ {
17
+ $carrier = Mage::getSingleton('gene_doddle/carrier');
18
+ return $carrier->getCarrierCode() . '_' . $carrier::METHOD_KEY;
19
+ }
20
+ }
app/code/community/Gene/Doddle/Model/Api/Doddle/Abstract.php ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Api_Doddle_Abstract
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ abstract class Gene_Doddle_Model_Api_Doddle_Abstract extends Mage_Core_Model_Abstract
9
+ {
10
+ /**
11
+ * Set the location of the API
12
+ */
13
+ const API_URL_XML_PATH = 'carriers/gene_doddle/stores_api';
14
+
15
+ /**
16
+ * Define the locations of our API information within Magento
17
+ */
18
+ const API_KEY_XML_PATH = 'carriers/gene_doddle/api_key';
19
+ const API_SECRET_XML_PATH = 'carriers/gene_doddle/api_secret';
20
+ const API_SCOPE_XML_PATH = 'carriers/gene_doddle/api_scope';
21
+
22
+ /**
23
+ * Location of our retailed ID
24
+ */
25
+ const RETAILED_ID_XML_PATH = 'carriers/gene_doddle/retailer_id';
26
+
27
+ /**
28
+ * The OAuth API URL
29
+ */
30
+ const API_ENVIRONMENT_XML_PATH = 'carriers/gene_doddle/environment';
31
+ const API_STAGING_OAUTH_XML_PATH = 'carriers/gene_doddle/staging_api';
32
+ const API_PRODUCTION_OAUTH_XML_PATH = 'carriers/gene_doddle/production_api';
33
+
34
+ /**
35
+ * Store the access token
36
+ * @var string
37
+ */
38
+ protected $accessToken = false;
39
+
40
+ /**
41
+ * Retrieve an access token from the powers that be
42
+ *
43
+ * @return string
44
+ */
45
+ public function getAccessToken($scope)
46
+ {
47
+ // Define the post data
48
+ $postData = array(
49
+ 'grant_type' => 'client_credentials',
50
+ 'scope' => $scope
51
+ );
52
+
53
+ // Make the request to the API
54
+ $http = $this->buildRequest('oauth/token', Varien_Http_Client::POST, $postData, true, false);
55
+
56
+ // Add in our auth
57
+ $http->setAuth(
58
+ Mage::getStoreConfig(self::API_KEY_XML_PATH),
59
+ Mage::getStoreConfig(self::API_SECRET_XML_PATH)
60
+ );
61
+
62
+ // Make the request
63
+ $response = $this->makeRequest($http);
64
+
65
+ // Verify we received an access token back
66
+ if(isset($response['access_token']) && !empty($response['access_token'])) {
67
+ $this->accessToken = $response['access_token'];
68
+ }
69
+
70
+ return $this->accessToken;
71
+ }
72
+
73
+ /**
74
+ * Function to making requests the API
75
+ *
76
+ * @param $call
77
+ */
78
+ protected function buildRequest($call, $method = Varien_Http_Client::GET, $postData = false, $oAuth = false, $headers = false)
79
+ {
80
+ // Grab a new instance of HTTP Client
81
+ $http = new Varien_Http_Client();
82
+
83
+ // Set the URI to the method
84
+ if($oAuth) {
85
+
86
+ // Build up our query array, Doddle requires api_key with all requests
87
+ $query = array(
88
+ 'api_key' => Mage::getStoreConfig(self::API_KEY_XML_PATH)
89
+ );
90
+
91
+ // Use the staging URL by default
92
+ $oauthUrl = Mage::getStoreConfig(self::API_STAGING_OAUTH_XML_PATH);
93
+
94
+ // If we're in production mode override with the production URL
95
+ if(Mage::getStoreConfig(self::API_ENVIRONMENT_XML_PATH) == Gene_Doddle_Model_System_Config_Environment::DODDLE_PRODUCTION) {
96
+ $oauthUrl = Mage::getStoreConfig(self::API_PRODUCTION_OAUTH_XML_PATH);
97
+ }
98
+
99
+ // Use the OAuth URL
100
+ $http->setUri($oauthUrl . $call . '?' . http_build_query($query));
101
+
102
+ } else {
103
+ $http->setUri(Mage::getStoreConfig(self::API_URL_XML_PATH) . $call);
104
+ }
105
+
106
+ // Set the method in, defaults to GET
107
+ $http->setMethod($method);
108
+
109
+ // Do we need to add in any post data?
110
+ if($method == Varien_Http_Client::POST) {
111
+ if (is_array($postData) && !empty($postData)) {
112
+
113
+ // Add in our post data
114
+ $http->setParameterPost($postData);
115
+
116
+ } else if (is_string($postData)) {
117
+
118
+ // Try and decode the string
119
+ try {
120
+
121
+ // Attempt to decode the JSON
122
+ $decode = Mage::helper('core')->jsonDecode($postData, Zend_Json::TYPE_ARRAY);
123
+
124
+ // Verify it decoded into an array
125
+ if ($decode && is_array($decode)) {
126
+
127
+ // Include the post data as the raw data body
128
+ $http->setRawData($postData, 'application/json')->request(Varien_Http_Client::POST);
129
+ }
130
+
131
+ } catch (Zend_Json_Exception $e) {
132
+ $this->_log($e);
133
+ return false;
134
+ }
135
+
136
+ }
137
+ }
138
+
139
+ // Are we attempting to add any headers into the request
140
+ if($headers && is_array($headers) && !empty($headers)) {
141
+
142
+ // Add in our headers
143
+ $http->setHeaders($headers);
144
+ }
145
+
146
+ // Return the HTTP body
147
+ return $http;
148
+ }
149
+
150
+ /**
151
+ * Make the request
152
+ *
153
+ * @param $http Varien_Http_Client
154
+ *
155
+ * @return bool|mixed
156
+ */
157
+ protected function makeRequest($http)
158
+ {
159
+ try {
160
+ // Make the request to the server
161
+ $response = $http->request();
162
+ } catch (Exception $e) {
163
+ $this->_log($e);
164
+ return false;
165
+ }
166
+
167
+ // Check the status of the request
168
+ if($response->getStatus() == 200) {
169
+
170
+ // Retrieve the raw body, which should be JSON
171
+ $body = $response->getRawBody();
172
+
173
+ // Catch any errors
174
+ try {
175
+
176
+ // Attempt to decode the response
177
+ $decodedBody = Mage::helper('core')->jsonDecode($body, Zend_Json::TYPE_ARRAY);
178
+
179
+ // Return the decoded response
180
+ return $decodedBody;
181
+
182
+ } catch (Zend_Json_Exception $e) {
183
+ $this->_log($e);
184
+ } catch (Exception $e) {
185
+ $this->_log($e);
186
+ }
187
+
188
+ } else {
189
+
190
+ // If the request is anything but a 200 response make a log of it
191
+ $this->_log($response->getStatus() . "\n" . $response->getRawBody());
192
+ }
193
+
194
+ return false;
195
+
196
+ }
197
+
198
+ /**
199
+ * Function to log anything we're wanting to log
200
+ *
201
+ * @param $data
202
+ */
203
+ protected function _log($data)
204
+ {
205
+ if($data instanceof Exception) {
206
+ $data = $data->getMessage() . "\n" . $data->getTraceAsString();
207
+ }
208
+ Mage::log($data, null, 'gene_doddle.log', true);
209
+ }
210
+
211
+ /**
212
+ * Build the scope string from an array
213
+ *
214
+ * @param $scope
215
+ *
216
+ * @return string
217
+ */
218
+ protected function buildScope($scope, $storeId)
219
+ {
220
+ // If we've not been given an array convert it over
221
+ if(!is_array($scope)) {
222
+ $scope = array($scope);
223
+ }
224
+
225
+ // Retrieve any extra scopes
226
+ $scopes = Mage::getStoreConfig(self::API_SCOPE_XML_PATH, $storeId);
227
+ if($scopes) {
228
+ // Convert the string into an array
229
+ $scopesArray = explode(' ', $scopes);
230
+
231
+ // Merge the array's
232
+ $scope = array_merge($scope, $scopesArray);
233
+ }
234
+
235
+ return implode(' ', $scope);
236
+ }
237
+ }
app/code/community/Gene/Doddle/Model/Api/Doddle/Preadvice.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Api_Doddle_Preadvice
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_Api_Doddle_Preadvice extends Gene_Doddle_Model_Api_Doddle_Abstract
9
+ {
10
+
11
+ /**
12
+ * Push an order through to Doddle
13
+ *
14
+ * @param \Mage_Sales_Model_Order $order
15
+ * @param \Mage_Sales_Model_Order_Shipment_Track $track
16
+ *
17
+ * @return bool
18
+ */
19
+ public function pushOrder(Mage_Sales_Model_Order $order, $tracks = false, $requestId = false)
20
+ {
21
+ // Verify we have an order
22
+ if($order->getId()) {
23
+
24
+ // Build our pre-advice array
25
+ $preAdvice = array(
26
+ 'retailerID' => Mage::getStoreConfig(self::RETAILED_ID_XML_PATH, $order->getStoreId()),
27
+ 'orderID' => $order->getId(),
28
+ 'customer' => $this->buildCustomer($order),
29
+ 'parcels' => $this->buildParcels($order, $tracks)
30
+ );
31
+
32
+ // Encode the request as JSON
33
+ $preAdviceJson = @json_encode($preAdvice);
34
+
35
+ // Check that we managed to build it into JSON
36
+ if($preAdviceJson) {
37
+
38
+ // Retrieve an access token from the API
39
+ if($accessToken = parent::getAccessToken($this->buildScope('preadvice', $order->getStoreId()))) {
40
+
41
+ // Build up our authorization
42
+ $headers = array(
43
+ 'Authorization' => 'Bearer ' . $accessToken,
44
+ 'Content-Type' => 'application/json'
45
+ );
46
+
47
+ // Build our HTTP request
48
+ $http = parent::buildRequest('parcels/preadvice', Varien_Http_Client::POST, $preAdviceJson, true, $headers);
49
+
50
+ // Make the request
51
+ $response = parent::makeRequest($http);
52
+
53
+ // Create a new request
54
+ $request = Mage::getModel('gene_doddle/request');
55
+
56
+ // Load the request if we have a request ID
57
+ if($requestId) {
58
+ $request->load($requestId);
59
+ }
60
+
61
+ $request->createRequest($order, $response);
62
+
63
+ // Return the status of the request
64
+ return $request->getStatus();
65
+
66
+ } else {
67
+ Mage::throwException('Unable to retrieve an access token from Doddle, please make sure the module\'s API settings are correctly configured.');
68
+ }
69
+
70
+ }
71
+
72
+ }
73
+
74
+ return false;
75
+ }
76
+
77
+ /**
78
+ * Build the customer aspect of the API request
79
+ *
80
+ * @param \Mage_Sales_Model_Order $order
81
+ *
82
+ * @return array
83
+ */
84
+ public function buildCustomer(Mage_Sales_Model_Order $order)
85
+ {
86
+ // Build up our customer array
87
+ $customer = array(
88
+ 'name' => array(
89
+ 'first' => $order->getBillingAddress()->getFirstname(),
90
+ 'last' => $order->getBillingAddress()->getLastname()
91
+ ),
92
+ 'address' => array(
93
+ 'streetAddress' => $order->getBillingAddress()->getStreet(),
94
+ 'town' => $order->getBillingAddress()->getCity(),
95
+ 'postCode' => $order->getBillingAddress()->getPostcode(),
96
+ 'countryCode' => $order->getBillingAddress()->getCountry()
97
+ ),
98
+ 'contact' => array(
99
+ 'emailAddress' => $order->getCustomerEmail(),
100
+ 'phoneNumber' => $order->getBillingAddress()->getTelephone()
101
+ ),
102
+ 'sendSMS' => true,
103
+ 'verification' => array()
104
+ );
105
+
106
+ // Verify we have prefix information first
107
+ if($prefix = $order->getBillingAddress()->getPrefix()) {
108
+ $customer['name']['prefix'] = $prefix;
109
+ }
110
+
111
+ // Attempt to determine whether a card was used for this transaction
112
+ if($order->getPayment()->getCcLast4()
113
+ && strlen($order->getPayment()->getCcLast4()) == 4
114
+ && $order->getPayment()->getCcType()) {
115
+
116
+ // This means they can use their card number as verification
117
+ $customer['verification'] = array(
118
+ 'type' => 'cardNumber',
119
+ 'value' => $order->getPayment()->getCcLast4()
120
+ );
121
+
122
+ } else {
123
+
124
+ // Otherwise the user will need to verify their postcode
125
+ $customer['verification'] = array(
126
+ 'type' => 'postCode',
127
+ 'value' => $order->getBillingAddress()->getPostcode()
128
+ );
129
+
130
+ }
131
+
132
+ return $customer;
133
+ }
134
+
135
+ /**
136
+ * Build up an array of parcels
137
+ *
138
+ * @param \Mage_Sales_Model_Order $order
139
+ *
140
+ * @return array
141
+ */
142
+ public function buildParcels(Mage_Sales_Model_Order $order, $tracks = false)
143
+ {
144
+ // Retrieve the tracking codes
145
+ $trackingCodes = $order->getTracksCollection();
146
+
147
+ // Are we only wanting to notify about a single order?
148
+ if($tracks && $tracks instanceof Mage_Sales_Model_Resource_Order_Shipment_Track_Collection) {
149
+
150
+ // Override the tracking codes
151
+ $trackingCodes = $tracks;
152
+ }
153
+
154
+ // Grab the parcel
155
+ $parcels = array();
156
+
157
+ // Loop through all the tracking codes
158
+ /* @var $trackingCode Mage_Sales_Model_Order_Shipment_Track */
159
+ foreach ($trackingCodes as $key => $trackingCode) {
160
+
161
+ // Start building our parcels
162
+ $parcels[] = $this->trackToParcel($trackingCode, $order);
163
+ }
164
+
165
+ return $parcels;
166
+
167
+ }
168
+
169
+ /**
170
+ * Convert a tracking code into a parcel
171
+ *
172
+ * @param \Mage_Sales_Model_Order_Shipment_Track $track
173
+ * @param \Mage_Sales_Model_Order $order
174
+ *
175
+ * @return array
176
+ */
177
+ protected function trackToParcel(Mage_Sales_Model_Order_Shipment_Track $track, Mage_Sales_Model_Order $order)
178
+ {
179
+ $parcel = array(
180
+ 'id' => $track->getNumber(),
181
+ 'storeID' => $order->getShippingAddress()->getDoddleStoreId()
182
+ );
183
+
184
+ // Add the weight information if we have it
185
+ if ($weight = $track->getWeight()) {
186
+ $parcel['weight'] = $weight;
187
+ }
188
+
189
+ return $parcel;
190
+ }
191
+
192
+ }
app/code/community/Gene/Doddle/Model/Api/Doddle/Stores.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Api_Doddle_Stores
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_Api_Doddle_Stores extends Gene_Doddle_Model_Api_Doddle_Abstract
9
+ {
10
+ /**
11
+ * Store an array of requested stores
12
+ * @var array
13
+ */
14
+ private $stores = false;
15
+
16
+ /**
17
+ * The location in the cache of our stores JSON
18
+ */
19
+ const DODDLE_STORE_CACHE_KEY = 'gene_doddle_stores';
20
+
21
+ /**
22
+ * Retrieve all the stores from the API
23
+ *
24
+ * @return array
25
+ */
26
+ public function getStores()
27
+ {
28
+ // Only even attempt to load the stores once
29
+ if(!$this->stores) {
30
+
31
+ // Attempt to load the stores from the cache
32
+ if($stores = $this->getCache()->load(self::DODDLE_STORE_CACHE_KEY)) {
33
+
34
+ // If they load from the cache then use those values
35
+ $this->stores = Mage::helper('core')->jsonDecode($stores);
36
+
37
+ } else {
38
+
39
+ // Otherwise make a request to the API
40
+ $http = parent::buildRequest('stores');
41
+ $this->stores = parent::makeRequest($http);
42
+
43
+ // Only update the cache if the request returns stores
44
+ if(!empty($this->stores)) {
45
+
46
+ // Store the stores within our cache
47
+ $this->getCache()->save(Mage::helper('core')->jsonEncode($this->stores), self::DODDLE_STORE_CACHE_KEY, array(self::DODDLE_STORE_CACHE_KEY), 60 * 60 * 24);
48
+ }
49
+
50
+ }
51
+ }
52
+
53
+ return $this->stores;
54
+ }
55
+
56
+ /**
57
+ * Return all stores as a collection
58
+ *
59
+ * @return \Varien_Data_Collection
60
+ */
61
+ public function getStoresCollection()
62
+ {
63
+ return $this->createStoreCollection($this->getStores());
64
+ }
65
+
66
+ /**
67
+ * Get the closest stores depending on long & lat
68
+ *
69
+ * @param $long
70
+ * @param $lat
71
+ * @param int $size
72
+ *
73
+ * @return array
74
+ */
75
+ public function getClosestStores($lat, $long, $size = 5)
76
+ {
77
+ // Push our location into an array
78
+ $userLocation = array($lat, $long);
79
+
80
+ // Grab the stores
81
+ $stores = $this->getStores();
82
+
83
+ // If we get no stores returned
84
+ if(empty($stores)) {
85
+ return false;
86
+ }
87
+
88
+ // Run through an array_map function
89
+ $distances = array_map(function($store) use($userLocation) {
90
+ $a = array($store['address']['lat'], $store['address']['long']);
91
+ return $this->distance($a, $userLocation);
92
+ }, $stores);
93
+
94
+ // Sort correctly
95
+ asort($distances);
96
+
97
+ // Get the closest stores
98
+ $closestStores = array();
99
+ foreach($distances as $key => $distance) {
100
+
101
+ // Add the distance into our array
102
+ $stores[$key]['distance'] = number_format($distance, 1);
103
+
104
+ // Add the store into our closestStores array
105
+ $closestStores[] = $stores[$key];
106
+
107
+ // Watch the size of the response
108
+ if(count($closestStores) == $size) {
109
+ break;
110
+ }
111
+ }
112
+
113
+ return $this->createStoreCollection($closestStores);
114
+ }
115
+
116
+ /**
117
+ * Return a singular store
118
+ *
119
+ * @param $storeId
120
+ *
121
+ * @return bool|mixed
122
+ */
123
+ public function getStore($storeId, $returnData = false)
124
+ {
125
+ // Retrieve the store from the API
126
+ $http = parent::buildRequest('stores/' . $storeId);
127
+ $store = parent::makeRequest($http);
128
+
129
+ // If the store loads and isn't false
130
+ if($store) {
131
+
132
+ // Do we just want the data?
133
+ if($returnData) {
134
+ return $store;
135
+ }
136
+
137
+ // Add the data into a model and return
138
+ return Mage::getModel('gene_doddle/store')->addData($store);
139
+ }
140
+
141
+ return false;
142
+ }
143
+
144
+ /**
145
+ * Build the store objects into Varien_Objects
146
+ *
147
+ * @param $stores
148
+ *
149
+ * @return \Varien_Data_Collection
150
+ * @throws \Exception
151
+ */
152
+ public function createStoreCollection($stores)
153
+ {
154
+ // Build a new basic collection
155
+ $collection = new Varien_Data_Collection();
156
+
157
+ // Loop through each store
158
+ foreach($stores as $store) {
159
+
160
+ // Create a new instance of the store model and append the data
161
+ $storeItem = Mage::getModel('gene_doddle/store')->addData($store);
162
+
163
+ // Add the item into our collection
164
+ $collection->addItem($storeItem);
165
+ }
166
+
167
+ return $collection;
168
+ }
169
+
170
+ /**
171
+ * Calculate the distance in miles between point A and B
172
+ *
173
+ * @param $a
174
+ * @param $b
175
+ *
176
+ * @return float
177
+ */
178
+ protected function distance($a, $b)
179
+ {
180
+ list($lat1, $lon1) = $a;
181
+ list($lat2, $lon2) = $b;
182
+
183
+ $theta = $lon1 - $lon2;
184
+ $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
185
+ $dist = acos($dist);
186
+ $dist = rad2deg($dist);
187
+ $miles = $dist * 60 * 1.1515;
188
+ return $miles;
189
+ }
190
+
191
+ /**
192
+ * Return an instance of the caching system
193
+ *
194
+ * @return \Zend_Cache_Core
195
+ */
196
+ protected function getCache()
197
+ {
198
+ return Mage::app()->getCache();
199
+ }
200
+ }
app/code/community/Gene/Doddle/Model/Carrier.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Carrier
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_Carrier
9
+ extends Mage_Shipping_Model_Carrier_Abstract
10
+ implements Mage_Shipping_Model_Carrier_Interface
11
+ {
12
+ /**
13
+ * Carrier's code, as defined in parent class
14
+ *
15
+ * @var string
16
+ */
17
+ protected $_code = 'gene_doddle';
18
+
19
+ /**
20
+ * Set the method key
21
+ */
22
+ const METHOD_KEY = 'collection';
23
+
24
+ /**
25
+ * Returns available shipping rates for Inchoo Shipping carrier
26
+ *
27
+ * @param Mage_Shipping_Model_Rate_Request $request
28
+ * @return Mage_Shipping_Model_Rate_Result
29
+ */
30
+ public function collectRates(Mage_Shipping_Model_Rate_Request $request)
31
+ {
32
+ /** @var Mage_Shipping_Model_Rate_Result $result */
33
+ $result = Mage::getModel('shipping/rate_result');
34
+
35
+ // Append our method
36
+ $result->append($this->_getStandardRate());
37
+
38
+ return $result;
39
+ }
40
+
41
+ /**
42
+ * Returns Allowed shipping methods
43
+ *
44
+ * @return array
45
+ */
46
+ public function getAllowedMethods()
47
+ {
48
+ return array(
49
+ self::METHOD_KEY => $this->getConfigData('name')
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Get Standard rate object
55
+ *
56
+ * @return Mage_Shipping_Model_Rate_Result_Method
57
+ */
58
+ protected function _getStandardRate()
59
+ {
60
+ /** @var Mage_Shipping_Model_Rate_Result_Method $rate */
61
+ $rate = Mage::getModel('shipping/rate_result_method');
62
+
63
+ $rate->setCarrier($this->_code);
64
+ $rate->setMethod('collection');
65
+
66
+ // If we're saving the payment, it means we're generating the review step
67
+ if(Mage::app()->getRequest()->getActionName() == 'savePayment' && Mage::getSingleton('checkout/session')->getDoddleStoreName()) {
68
+ $rate->setCarrierTitle(Mage::helper('gene_doddle')->__('Collect from Doodle'));
69
+ $rate->setMethodTitle(Mage::getSingleton('checkout/session')->getDoddleStoreName());
70
+ } else {
71
+ $rate->setCarrierTitle($this->getConfigData('title'));
72
+ $rate->setMethodTitle($this->getConfigData('name'));
73
+ }
74
+
75
+ $rate->setPrice($this->getConfigData('price'));
76
+ $rate->setCost(0);
77
+
78
+ return $rate;
79
+ }
80
+
81
+ }
app/code/community/Gene/Doddle/Model/Observer.php ADDED
@@ -0,0 +1,482 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Observer
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_Observer
9
+ {
10
+ /**
11
+ * Get one page checkout model
12
+ *
13
+ * @return Mage_Checkout_Model_Type_Onepage
14
+ */
15
+ protected function getSession()
16
+ {
17
+ return Mage::getSingleton('checkout/session');
18
+ }
19
+
20
+ /**
21
+ * Insert the Doddle block into the shipping methods step
22
+ *
23
+ * @param \Varien_Event_Observer $observer
24
+ *
25
+ * @return $this
26
+ */
27
+ public function insertDoddleMarkup(Varien_Event_Observer $observer)
28
+ {
29
+ /* @var $block Mage_Checkout_Block_Onepage_Shipping_Method_Available */
30
+ $block = $observer->getBlock();
31
+
32
+ // Are we dealing with the shipping method available section
33
+ if($block instanceof Mage_Checkout_Block_Onepage_Shipping_Method_Available) {
34
+
35
+ // Grab the transport object
36
+ $transport = $observer->getTransport();
37
+
38
+ // Append our blocks HTML onto the end of the generated HTML
39
+ $html = (!Mage::registry('doddle_only') ? $transport->getHtml() : '') . Mage::app()
40
+ ->getLayout()
41
+ ->createBlock('gene_doddle/onepage_shipping_method_doddle')
42
+ ->setParentBlock($block)
43
+ ->toHtml();
44
+
45
+ // Set the HTML back into the transport object
46
+ $transport->setHtml($html);
47
+
48
+ }
49
+
50
+ return $this;
51
+ }
52
+
53
+ /**
54
+ * Swap out the progress template if we're using Doddle
55
+ *
56
+ * @param \Varien_Event_Observer $observer
57
+ *
58
+ * @return $this
59
+ */
60
+ public function modifyShippingMethodProgress(Varien_Event_Observer $observer)
61
+ {
62
+ /* @var $block Mage_Checkout_Block_Onepage_Progress */
63
+ $block = $observer->getBlock();
64
+
65
+ // If we're within progress and the shipping method
66
+ if($block instanceof Mage_Checkout_Block_Onepage_Progress && $block->getTemplate() == 'checkout/onepage/progress/shipping_method.phtml') {
67
+
68
+ // Check we're using Doddle
69
+ if($block->getShippingMethod() == Mage::helper('gene_doddle')->getShippingMethodCode()) {
70
+
71
+ // The old switcheroo
72
+ $block->setTemplate('gene/doddle/onepage/progress/shipping_method.phtml');
73
+
74
+ }
75
+
76
+ }
77
+
78
+ return $this;
79
+ }
80
+
81
+ /**
82
+ * Capture the users selected store choice if they've choosen to use Doddle
83
+ *
84
+ * @param \Varien_Event_Observer $observer
85
+ *
86
+ * @return $this
87
+ */
88
+ public function captureStoreSelection(Varien_Event_Observer $observer)
89
+ {
90
+ /* @var $request Zend_Controller_Request_Abstract */
91
+ $request = $observer->getRequest();
92
+
93
+ /* @var $quote Mage_Sales_Model_Quote */
94
+ $quote = $observer->getQuote();
95
+
96
+ // Has the use selected to use Doddle?
97
+ if($quote->getShippingAddress()->getShippingMethod() == Mage::helper('gene_doddle')->getShippingMethodCode()) {
98
+
99
+ // Grab the store ID
100
+ $storeId = $request->getParam('doddle-store');
101
+
102
+ // If it's not empty attempt to load the store
103
+ if(!empty($storeId)) {
104
+
105
+ // Attempt to load the store
106
+ $store = Mage::getModel('gene_doddle/store')->load($storeId);
107
+ }
108
+
109
+ // Validate both potential failures at once
110
+ if(empty($storeId) || isset($store) && !$store) {
111
+
112
+ // Build our result array
113
+ $result = array(
114
+ 'error' => -1,
115
+ 'message' => Mage::helper('gene_doddle')->__('The Doddle store you\'ve selected is no longer available, please try and locate your nearest store again.')
116
+ );
117
+
118
+ // Send it over
119
+ Mage::app()->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
120
+ Mage::app()->getResponse()->sendResponse();
121
+ exit;
122
+ }
123
+
124
+ // Set the store ID within the session
125
+ Mage::getSingleton('checkout/session')->setDoddleStoreId($storeId)->setDoddleStoreName($store->getName());
126
+
127
+ } else {
128
+
129
+ // Otherwise make sure this data isn't present in the session
130
+ Mage::getSingleton('checkout/session')->unsDoddleStoreId()->unsDoddleStoreName();
131
+ }
132
+
133
+ return $this;
134
+ }
135
+
136
+ /**
137
+ * Just before the order is saved we want to swap the shipping
138
+ * address out to the Doddle address
139
+ *
140
+ * @param \Varien_Event_Observer $observer
141
+ *
142
+ * @return $this
143
+ */
144
+ public function changeToDoddleAddress(Varien_Event_Observer $observer)
145
+ {
146
+ /* @var $order Mage_Sales_Model_Order */
147
+ $order = $observer->getOrder();
148
+
149
+ /* @var $quote Mage_Sales_Model_Quote */
150
+ $quote = $observer->getQuote();
151
+
152
+ // Has the use selected to use Doddle?
153
+ if($order->getShippingMethod() == Mage::helper('gene_doddle')->getShippingMethodCode()) {
154
+
155
+ // Retrieve the store ID from the session
156
+ $storeId = Mage::getSingleton('checkout/session')->getDoddleStoreId();
157
+
158
+ // Verify we've got a store ID
159
+ if(!$storeId) {
160
+ Mage::throwException(Mage::helper('gene_doddle')->__('No store has been selected for collection from Doddle, please try again.'));
161
+ }
162
+
163
+ // Load up the store
164
+ /* @var $store Gene_Doddle_Model_Store */
165
+ $store = Mage::getModel('gene_doddle/store')->load($storeId);
166
+
167
+ // Check the store can load
168
+ if(!$store) {
169
+ Mage::throwException(Mage::helper('gene_doddle')->__('The Doddle store you\'ve selected is no longer available, please try and locate your nearest store again.'));
170
+ }
171
+
172
+ // Change the address shipping address
173
+ $order->getShippingAddress()->addData($store->getMagentoShippingAddress());
174
+
175
+ // This order can no longer be shipped partially as Doddle has to receive it all at once
176
+ $order->setCanShipPartially(0)->setCanShipPartiallyItem(0);
177
+ $order->save();
178
+
179
+ // Update the quote
180
+ $quote->getShippingAddress()->addData($store->getMagentoShippingAddress());
181
+ $quote->save();
182
+
183
+ }
184
+
185
+ return $this;
186
+ }
187
+
188
+ /**
189
+ * Insert a third option on the billing information step
190
+ *
191
+ * @param \Varien_Event_Observer $observer
192
+ *
193
+ * @return $this
194
+ */
195
+ public function insertShipToDoddle(Varien_Event_Observer $observer)
196
+ {
197
+ /* @var $block Mage_Checkout_Block_Onepage_Billing */
198
+ $block = $observer->getBlock();
199
+
200
+ // Check we're within the correct block
201
+ if(Mage::getStoreConfigFlag('carriers/gene_doddle/active') && $block instanceof Mage_Checkout_Block_Onepage_Billing) {
202
+
203
+ // Grab the transport object
204
+ $transport = $observer->getTransport();
205
+
206
+ // We need to convert the string as we have some special characters
207
+ $html = mb_convert_encoding($transport->getHtml(), 'HTML-ENTITIES', "UTF-8");
208
+
209
+ // Use DomQuery for jQuery like searching
210
+ $zendDomQuery = new Zend_Dom_Query($html);
211
+
212
+ // Find the "no" use shipping address option
213
+ $useShippingNo = $zendDomQuery->query('input[id="billing:use_for_shipping_no"]');
214
+
215
+ // Verify we found the element
216
+ if($useShippingNo->count()) {
217
+
218
+ // Retrieve the parent LI
219
+ $parentLi = $useShippingNo->current()->parentNode;
220
+
221
+ // Retrieve out Doddle block
222
+ $doddleOption = Mage::app()->getLayout()->createBlock('gene_doddle/onepage_billing_option')->toHtml();
223
+
224
+ // We have to use a fragment to get the HTML to output
225
+ $fragment = $useShippingNo->getDocument()->createDocumentFragment();
226
+ $fragment->appendXML($doddleOption);
227
+
228
+ // Append the fragment
229
+ $parentLi->appendChild($fragment);
230
+
231
+ // Set the HTML as our new content
232
+ $transport->setHtml($useShippingNo->getDocument()->saveHTML());
233
+
234
+ }
235
+
236
+ }
237
+
238
+ return $this;
239
+ }
240
+
241
+ /**
242
+ * Intercept the saveBilling action to allow the user to skip shipping information
243
+ *
244
+ * @param \Varien_Event_Observer $observer
245
+ */
246
+ public function skipShippingInformation(Varien_Event_Observer $observer)
247
+ {
248
+ /** @var Mage_Checkout_OnepageController $controllerAction */
249
+ $controllerAction = $observer->getControllerAction();
250
+
251
+ // Grab the data from the request
252
+ $billing = $controllerAction->getRequest()->getPost('billing', array());
253
+
254
+ // Check to see if the user has selected Doddle
255
+ if(isset($billing['use_for_shipping']) && $billing['use_for_shipping'] == 'doddle') {
256
+
257
+ // Set to 1
258
+ $billing['use_for_shipping'] = 1;
259
+
260
+ // If so we do want to actually use the shipping in place of billing
261
+ $controllerAction->getRequest()->setPost('billing', $billing);
262
+
263
+ // Set something within the session so everyone is aware
264
+ Mage::register('doddle_only', true);
265
+ }
266
+
267
+ }
268
+
269
+ /**
270
+ * Replace the shipping method info box with Doddle information
271
+ *
272
+ * @param \Varien_Event_Observer $observer
273
+ *
274
+ * @return $this
275
+ */
276
+ public function replaceShippingMethodInfo(Varien_Event_Observer $observer)
277
+ {
278
+ /* @var $block Mage_Adminhtml_Block_Sales_Order_View_Tab_Info */
279
+ $block = $observer->getBlock();
280
+
281
+ // Check we're within the correct block
282
+ if($block instanceof Mage_Adminhtml_Block_Sales_Order_View_Tab_Info) {
283
+
284
+ // Verify we have a loaded order
285
+ if($order = Mage::registry('current_order')) {
286
+
287
+ // Verify that this order actually used Doddle
288
+ if($order->getShippingMethod() == Mage::helper('gene_doddle')->getShippingMethodCode()) {
289
+
290
+ // Grab the transport object
291
+ $transport = $observer->getTransport();
292
+
293
+ // We need to convert the string as we have some special characters
294
+ $html = mb_convert_encoding($transport->getHtml(), 'HTML-ENTITIES', "UTF-8");
295
+
296
+ // Use DomQuery for jQuery like searching
297
+ $zendDomQuery = new Zend_Dom_Query($html);
298
+
299
+ // Grab the shipping address heading
300
+ $shippingAddressHead = $zendDomQuery->query('.head-shipping-method');
301
+
302
+ // Verify we can match onto that
303
+ if ($shippingAddressHead->count()) {
304
+
305
+ // Retrieve the title
306
+ $title = $shippingAddressHead->current();
307
+
308
+ // Grab the fieldset
309
+ $fieldset = $title->parentNode->parentNode->getElementsByTagName('fieldset');
310
+
311
+ // Verify we have found it
312
+ if ($fieldset->length) {
313
+
314
+ // Retrieve the first item in the element list
315
+ $fieldsetElement = $fieldset->item(0);
316
+
317
+ // Remove the old junk
318
+ while ($fieldsetElement->childNodes->length) {
319
+ $fieldsetElement->removeChild($fieldsetElement->firstChild);
320
+ }
321
+
322
+ // Retrieve out Doddle block
323
+ $doddleBlock = Mage::app()->getLayout()->createBlock('gene_doddle/adminhtml_sales_order_view_shippingmethod')->toHtml();
324
+
325
+ // We have to use a fragment to get the HTML to output
326
+ $fragment = $shippingAddressHead->getDocument()->createDocumentFragment();
327
+ $fragment->appendXML($doddleBlock);
328
+
329
+ // Append the fragment
330
+ $fieldsetElement->appendChild($fragment);
331
+
332
+ // Set the HTML as our new content
333
+ $transport->setHtml($shippingAddressHead->getDocument()->saveHTML());
334
+
335
+ }
336
+ }
337
+
338
+ }
339
+
340
+ }
341
+
342
+ }
343
+
344
+ return $this;
345
+ }
346
+
347
+ /**
348
+ * Inform Doddle about an order dispatched
349
+ *
350
+ * @param \Varien_Event_Observer $observer
351
+ *
352
+ * @return $this
353
+ */
354
+ public function informDoddle(Varien_Event_Observer $observer)
355
+ {
356
+ /* @var $shipment Mage_Sales_Model_Order_Shipment */
357
+ $shipment = $observer->getShipment();
358
+
359
+ // Retrieve the order
360
+ $order = $shipment->getOrder();
361
+
362
+ // Verify this order is from Doddle
363
+ if($order->getShippingMethod() == Mage::helper('gene_doddle')->getShippingMethodCode()) {
364
+
365
+ // Verify at least one tracking number has been added
366
+ if(count($shipment->getAllTracks()) == 0) {
367
+ Mage::throwException('Doddle requires at least one tracking code to be supplied, multiple tracking codes can be supplied if the order was shipped seperately.');
368
+ }
369
+
370
+ // Grab an instance of the API
371
+ $preAdviceApi = Mage::getSingleton('gene_doddle/api_doddle_preadvice');
372
+
373
+ // Attempt to push the order to Doddle and retrieve the status
374
+ $status = $preAdviceApi->pushOrder($order, $shipment->getTracksCollection());
375
+
376
+ // Push the order over to Doddle
377
+ if($status == Gene_Doddle_Model_Request::STATUS_COMPLETE) {
378
+
379
+ // Add in our session message
380
+ Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('gene_doddle')->__('Pre-advice has been successfully sent to Doddle.'));
381
+
382
+ } else if($status == Gene_Doddle_Model_Request::STATUS_QUEUE) {
383
+
384
+ // Inform the user the request failed but it's okay
385
+ Mage::getSingleton('adminhtml/session')->addNotice(Mage::helper('gene_doddle')->__('We were unable to connect with Doddle to send pre-advice information, the request has been queued.'));
386
+
387
+ } else {
388
+
389
+ // This means something went really wrong
390
+ Mage::throwException('A severe error has occurred whilst trying to advise Doddle of the shipment.');
391
+
392
+ }
393
+
394
+ }
395
+
396
+ return $this;
397
+ }
398
+
399
+ /**
400
+ * Attempt to run any API requests left in the queue
401
+ *
402
+ * @return $this
403
+ */
404
+ public function runQueue()
405
+ {
406
+ // Retrieve all queued requests
407
+ $queuedOrders = Mage::getResourceModel('sales/order_collection');
408
+
409
+ // Add in total sales amounts
410
+ $queuedOrders->getSelect()->joinInner(
411
+ array('doddle' => $queuedOrders->getTable('gene_doddle/request')),
412
+ 'main_table.entity_id = doddle.order_id AND doddle.status = \'' . Gene_Doddle_Model_Request::STATUS_QUEUE . '\'',
413
+ array('doddle_status' => 'doddle.status', 'doddle.request_id')
414
+ )->group('main_table.entity_id');
415
+
416
+ // If we have more then one queued items let's get processing
417
+ if($queuedOrders->count() >= 1) {
418
+
419
+ // Grab an instance of the API
420
+ $preAdviceApi = Mage::getSingleton('gene_doddle/api_doddle_preadvice');
421
+
422
+ // Loop through each of the orders
423
+ foreach($queuedOrders as $order) {
424
+
425
+ // Check the order has shipments
426
+ if($order->hasShipments()) {
427
+
428
+ // Loop through the collection
429
+ /* @var $shipment Mage_Sales_Model_Order_Shipment */
430
+ foreach ($order->getShipmentsCollection() as $shipment) {
431
+
432
+ // Verify at least one tracking number has been added
433
+ if (count($shipment->getAllTracks()) == 0) {
434
+
435
+ // Update the request to failed
436
+ $this->updateRequest($order->getData('request_id'), 'No tracking information for shipment', Gene_Doddle_Model_Request::STATUS_FAILED);
437
+
438
+ } else {
439
+
440
+ // Attempt to push the order to Doddle
441
+ $preAdviceApi->pushOrder($order, $shipment->getTracksCollection(), $order->getData('request_id'));
442
+
443
+ }
444
+
445
+ }
446
+
447
+ } else {
448
+
449
+ // Update the request to failed
450
+ $this->updateRequest($order->getData('request_id'), 'Order has no shipments', Gene_Doddle_Model_Request::STATUS_FAILED);
451
+
452
+ }
453
+
454
+ }
455
+
456
+ }
457
+
458
+ return $this;
459
+ }
460
+
461
+ /**
462
+ * Update a request with more information
463
+ *
464
+ * @param $requestId
465
+ * @param $message
466
+ * @param $status
467
+ *
468
+ * @return \Gene_Doddle_Model_Request
469
+ * @throws \Exception
470
+ */
471
+ private function updateRequest($requestId, $message, $status)
472
+ {
473
+ // Otherwise update the request
474
+ $request = Mage::getModel('gene_doddle/request')->load($requestId);
475
+ $request->addData(array(
476
+ 'message' => Mage::helper('gene_doddle')->__($message),
477
+ 'status' => $status
478
+ ));
479
+ return $request->save();
480
+ }
481
+
482
+ }
app/code/community/Gene/Doddle/Model/Request.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Request
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_Request extends Mage_Core_Model_Abstract
9
+ {
10
+
11
+ /**
12
+ * Queue statuses
13
+ */
14
+ const STATUS_QUEUE = 'queue';
15
+ const STATUS_FAILED = 'failed';
16
+ const STATUS_COMPLETE = 'complete';
17
+
18
+ protected function _construct()
19
+ {
20
+ $this->_init('gene_doddle/request');
21
+ }
22
+
23
+ /**
24
+ * Update the rows dates accordingly
25
+ */
26
+ protected function _beforeSave()
27
+ {
28
+ parent::_beforeSave();
29
+
30
+ // If the object is new set the created at date
31
+ if($this->isObjectNew()) {
32
+ $this->setCreatedAt(Mage::getSingleton('core/date')->gmtDate());
33
+ }
34
+
35
+ // Always set the updated at date
36
+ $this->setUpdatedAt(Mage::getSingleton('core/date')->gmtDate());
37
+ }
38
+
39
+ /**
40
+ * Create a new item from data
41
+ *
42
+ * @param \Mage_Sales_Model_Order $order
43
+ * @param $request
44
+ */
45
+ public function createRequest(Mage_Sales_Model_Order $order, $response)
46
+ {
47
+ if(!$order->getId()) {
48
+ return false;
49
+ }
50
+
51
+ // Set the order ID
52
+ $this->addData(array(
53
+ 'order_id' => $order->getId()
54
+ ));
55
+
56
+ // If we have an ID the request was a success
57
+ if(isset($response['id']) && !empty($response['id'])) {
58
+ $this->addData(array(
59
+ 'preadvice_id' => $response['id'],
60
+ 'status' => self::STATUS_COMPLETE
61
+ ));
62
+ } else {
63
+ // If the request has failed attempt to queue the request
64
+ $this->addData(array(
65
+ 'status' => self::STATUS_QUEUE,
66
+ 'message' => $this->getErrorsAsString($response)
67
+ ));
68
+ }
69
+
70
+ $this->save();
71
+ return $this;
72
+ }
73
+
74
+ /**
75
+ * Return the errors from a response as a string
76
+ *
77
+ * @param $response
78
+ *
79
+ * @return bool|string
80
+ */
81
+ public function getErrorsAsString($response)
82
+ {
83
+ // Check to see whether we have an array
84
+ if(isset($response['errors']) && is_array($response['errors'])) {
85
+
86
+ // Iterate through the errors and build up an array
87
+ $errors = false;
88
+ foreach($response['errors'] as $error) {
89
+ if(isset($error['code']) && isset($error['message'])) {
90
+ $errors[] = $error['code'] . ': ' . $error['message'];
91
+ }
92
+ }
93
+
94
+ return implode(', ', $errors);
95
+
96
+ } else if(isset($response['errors']) && is_string($response['errors'])) {
97
+ return $response['errors'];
98
+ }
99
+
100
+ return false;
101
+ }
102
+
103
+ }
app/code/community/Gene/Doddle/Model/Resource/Request.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Resource_Request
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_Resource_Request extends Mage_Core_Model_Resource_Db_Abstract
9
+ {
10
+
11
+ protected function _construct()
12
+ {
13
+ $this->_init('gene_doddle/request', 'request_id');
14
+ }
15
+
16
+ }
app/code/community/Gene/Doddle/Model/Resource/Request/Collection.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Resource_Request_Collection
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_Resource_Request_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
9
+ {
10
+
11
+ protected function _construct()
12
+ {
13
+ $this->_init('gene_doddle/request');
14
+ }
15
+
16
+ }
app/code/community/Gene/Doddle/Model/Store.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_Store
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_Store extends Varien_Object
9
+ {
10
+
11
+ /**
12
+ * Return a formatted version of the address
13
+ *
14
+ * @return bool|string
15
+ */
16
+ public function getAddress()
17
+ {
18
+ if($this->getId()) {
19
+
20
+ // Grab the address from the stores data
21
+ $addressData = $this->getData('address');
22
+
23
+ // Build up an array of address elements
24
+ $addressElements = array();
25
+
26
+ // Implode the street address
27
+ if(isset($addressData['streetAddress']) && is_array($addressData['streetAddress'])) {
28
+ $addressElements = array_filter($addressData['streetAddress']);
29
+ }
30
+
31
+ // Include the town
32
+ if(isset($addressData['town']) && !empty($addressData['town'])) {
33
+ $addressElements[] = $addressData['town'];
34
+ }
35
+
36
+ return implode(', ', $addressElements);
37
+ }
38
+ return false;
39
+ }
40
+
41
+ /**
42
+ * Return the stores latitude
43
+ *
44
+ * @return bool|mixed
45
+ */
46
+ public function getLat()
47
+ {
48
+ if($this->getId()) {
49
+
50
+ // Grab the address from the stores data
51
+ return $this->getData('address/lat');
52
+
53
+ }
54
+
55
+ return false;
56
+ }
57
+
58
+ /**
59
+ * Return the stores longitude
60
+ *
61
+ * @return bool|mixed
62
+ */
63
+ public function getLong()
64
+ {
65
+ if($this->getId()) {
66
+
67
+ // Grab the address from the stores data
68
+ return $this->getData('address/long');
69
+
70
+ }
71
+
72
+ return false;
73
+ }
74
+
75
+ /**
76
+ * Return an array correctly formatted for Magento
77
+ *
78
+ * @return array
79
+ */
80
+ public function getMagentoShippingAddress()
81
+ {
82
+ if($this->getId()) {
83
+
84
+ // Grab the address from the stores data
85
+ $addressData = $this->getData('address');
86
+
87
+ // Build up an array of address elements
88
+ $address = array(
89
+ 'firstname' => 'Doddle',
90
+ 'lastname' => $this->getData('name'),
91
+ 'company' => '',
92
+ 'street' => implode("\n", array_filter($addressData['streetAddress'])),
93
+ 'city' => $addressData['town'],
94
+ 'region' => $addressData['county'],
95
+ 'postcode' => $addressData['postCode'],
96
+ 'country_id' => $addressData['countryCode'],
97
+ 'telephone' => $this->getData('phoneNumber'),
98
+ 'doddle_store_id' => $this->getId(),
99
+ 'same_as_billing' => 0,
100
+ 'save_in_address_book' => 0
101
+ );
102
+
103
+ // Some data seems to just have comma's set, so attempt to cleanse these
104
+ foreach($address as $key => $addressItem) {
105
+ if($addressItem == ',') {
106
+ $address[$key] = '';
107
+ }
108
+ }
109
+
110
+ return $address;
111
+
112
+ }
113
+
114
+ return false;
115
+ }
116
+
117
+ /**
118
+ * Return the stores opening times as a string
119
+ *
120
+ * @return bool|array
121
+ */
122
+ public function getOpeningTimes()
123
+ {
124
+ if($this->getId()) {
125
+
126
+ // Retrieve the opening times
127
+ $openingTimes = $this->getData('openingTimes');
128
+
129
+ // Verify we have some opening times
130
+ if(!empty($openingTimes)) {
131
+
132
+ // Build up the array
133
+ $response = array();
134
+ foreach($openingTimes as $day => $openingTime) {
135
+
136
+ // Build the time up
137
+ $time = $openingTime['open'] . ' - ' . $openingTime['close'];
138
+
139
+ // If the store is closed on this day make it known
140
+ if($openingTime['open'] == '00:00' && $openingTime['close'] == '00:00') {
141
+ $time = false;
142
+ }
143
+
144
+ $response[] = array(
145
+ 'label' => ucfirst(preg_replace('/\B([A-Z])/', ' $1', $day)),
146
+ 'value' => $time
147
+ );
148
+ }
149
+
150
+ // Implode with a br
151
+ return $response;
152
+
153
+ }
154
+
155
+ }
156
+
157
+ return false;
158
+ }
159
+
160
+ /**
161
+ * Mimic Magento core functionality with load() function
162
+ *
163
+ * @param $id
164
+ *
165
+ * @return $this
166
+ */
167
+ public function load($id)
168
+ {
169
+ $storeData = $this->getApi()->getStore($id, true);
170
+ if($storeData) {
171
+ $this->addData($storeData);
172
+ }
173
+
174
+ return $this;
175
+ }
176
+
177
+ /**
178
+ * Retrieve an instance of the API
179
+ *
180
+ * @return Gene_Doddle_Model_Api_Doddle_Stores
181
+ */
182
+ protected function getApi()
183
+ {
184
+ return Mage::getSingleton('gene_doddle/api_doddle_stores');
185
+ }
186
+
187
+ }
app/code/community/Gene/Doddle/Model/System/Config/Environment.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_Model_System_Config_Environment
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_Model_System_Config_Environment
9
+ {
10
+
11
+ const DODDLE_STAGING = 'staging';
12
+ const DODDLE_PRODUCTION = 'production';
13
+
14
+ /**
15
+ * Return the environment options as an array
16
+ *
17
+ * @return array
18
+ */
19
+ public function toOptionArray()
20
+ {
21
+ return array(
22
+ array('value'=> self::DODDLE_STAGING, 'label'=> Mage::helper('gene_doddle')->__('Staging')),
23
+ array('value'=> self::DODDLE_PRODUCTION, 'label'=>Mage::helper('gene_doddle')->__('Production')),
24
+ );
25
+ }
26
+ }
app/code/community/Gene/Doddle/controllers/IndexController.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Gene_Doddle_IndexController
5
+ *
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ class Gene_Doddle_IndexController extends Mage_Core_Controller_Front_Action
9
+ {
10
+
11
+ /**
12
+ * Return the table for the closest 5 stores
13
+ *
14
+ * @return bool
15
+ */
16
+ public function getClosestLongLatAction()
17
+ {
18
+ // Grab the long and lat from the request
19
+ $long = $this->getRequest()->getParam('long');
20
+ $lat = $this->getRequest()->getParam('lat');
21
+
22
+ // Verify they're both set and not false
23
+ if(!$long || !$lat) {
24
+ return $this->returnAsJson(array('error' => $this->__('You must specify both latitude and longitude to use this action.')));
25
+ }
26
+
27
+ // Load up the layout
28
+ $this->loadLayout();
29
+
30
+ // Return a formatted JSON response
31
+ return $this->returnAsJson(array(
32
+ 'success' => 'true',
33
+ 'html' => $this->getLayout()->getOutput()
34
+ ));
35
+ }
36
+
37
+ /**
38
+ * Return JSON to the browser
39
+ *
40
+ * @param $json
41
+ *
42
+ * @return bool
43
+ */
44
+ protected function returnAsJson($json)
45
+ {
46
+ // Set the response
47
+ $this->getResponse()->setHeader('Content-type', 'application/json');
48
+ $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($json));
49
+ return false;
50
+ }
51
+
52
+ }
app/code/community/Gene/Doddle/etc/config.xml ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Gene_Doddle>
5
+ <version>1.0.0</version>
6
+ </Gene_Doddle>
7
+ </modules>
8
+
9
+ <global>
10
+ <models>
11
+ <gene_doddle>
12
+ <class>Gene_Doddle_Model</class>
13
+ <resourceModel>gene_doddle_resource</resourceModel>
14
+ </gene_doddle>
15
+ <gene_doddle_resource>
16
+ <class>Gene_Doddle_Model_Resource</class>
17
+ <entities>
18
+ <request>
19
+ <table>gene_doddle_request</table>
20
+ </request>
21
+ </entities>
22
+ </gene_doddle_resource>
23
+ </models>
24
+
25
+ <blocks>
26
+ <gene_doddle>
27
+ <class>Gene_Doddle_Block</class>
28
+ </gene_doddle>
29
+ </blocks>
30
+
31
+ <helpers>
32
+ <gene_doddle>
33
+ <class>Gene_Doddle_Helper</class>
34
+ </gene_doddle>
35
+ </helpers>
36
+
37
+ <resources>
38
+ <gene_doddle_setup>
39
+ <setup>
40
+ <module>Gene_Doddle</module>
41
+ <class>Mage_Sales_Model_Resource_Setup</class>
42
+ </setup>
43
+ </gene_doddle_setup>
44
+ </resources>
45
+
46
+ <events>
47
+
48
+ <!-- Capture shipment information being adding into the shipment -->
49
+ <sales_order_shipment_save_before>
50
+ <observers>
51
+ <gene_doddle_inform_doddle>
52
+ <type>singleton</type>
53
+ <class>gene_doddle/observer</class>
54
+ <method>informDoddle</method>
55
+ </gene_doddle_inform_doddle>
56
+ </observers>
57
+ </sales_order_shipment_save_before>
58
+
59
+ </events>
60
+ </global>
61
+
62
+ <frontend>
63
+ <layout>
64
+ <updates>
65
+ <gene_doddle module="Gene_Doddle">
66
+ <file>gene/doddle.xml</file>
67
+ </gene_doddle>
68
+ </updates>
69
+ </layout>
70
+
71
+ <routers>
72
+ <doddle>
73
+ <use>standard</use>
74
+ <args>
75
+ <module>Gene_Doddle</module>
76
+ <frontName>doddle</frontName>
77
+ </args>
78
+ </doddle>
79
+ </routers>
80
+
81
+ <events>
82
+
83
+ <!-- Enable user to skip shipping information step -->
84
+ <core_block_abstract_to_html_after>
85
+ <observers>
86
+ <gene_doddle_shipping_method>
87
+ <class>gene_doddle/observer</class>
88
+ <method>insertShipToDoddle</method>
89
+ </gene_doddle_shipping_method>
90
+ </observers>
91
+ </core_block_abstract_to_html_after>
92
+
93
+ <controller_action_predispatch_checkout_onepage_saveBilling>
94
+ <observers>
95
+ <gene_doddle_skip_shipping>
96
+ <class>gene_doddle/observer</class>
97
+ <method>skipShippingInformation</method>
98
+ </gene_doddle_skip_shipping>
99
+ </observers>
100
+ </controller_action_predispatch_checkout_onepage_saveBilling>
101
+
102
+ <!-- Insert the Doddle mark up after the available shipping methods -->
103
+ <core_block_abstract_to_html_after>
104
+ <observers>
105
+ <gene_doddle_insert_checkout>
106
+ <type>singleton</type>
107
+ <class>gene_doddle/observer</class>
108
+ <method>insertDoddleMarkup</method>
109
+ </gene_doddle_insert_checkout>
110
+ </observers>
111
+ </core_block_abstract_to_html_after>
112
+
113
+ <!-- Alter the progress block on the default Enterprise checkout -->
114
+ <core_block_abstract_to_html_before>
115
+ <observers>
116
+ <gene_doddle_change_progress>
117
+ <type>singleton</type>
118
+ <class>gene_doddle/observer</class>
119
+ <method>modifyShippingMethodProgress</method>
120
+ </gene_doddle_change_progress>
121
+ </observers>
122
+ </core_block_abstract_to_html_before>
123
+
124
+ <!-- When the user saved the shipping method capture the selected store -->
125
+ <checkout_controller_onepage_save_shipping_method>
126
+ <observers>
127
+ <gene_doddle_capture_store>
128
+ <type>singleton</type>
129
+ <class>gene_doddle/observer</class>
130
+ <method>captureStoreSelection</method>
131
+ </gene_doddle_capture_store>
132
+ </observers>
133
+ </checkout_controller_onepage_save_shipping_method>
134
+
135
+ <!-- Just before the quote is submitted we want to swap the shipping address to the Doddle store -->
136
+ <sales_model_service_quote_submit_before>
137
+ <observers>
138
+ <gene_doddle_swap_address>
139
+ <type>singleton</type>
140
+ <class>gene_doddle/observer</class>
141
+ <method>changeToDoddleAddress</method>
142
+ </gene_doddle_swap_address>
143
+ </observers>
144
+ </sales_model_service_quote_submit_before>
145
+
146
+ </events>
147
+ </frontend>
148
+
149
+ <adminhtml>
150
+ <events>
151
+
152
+ <!-- Modify the shipping method information within the admin -->
153
+ <core_block_abstract_to_html_after>
154
+ <observers>
155
+ <gene_doddle_shipping_method>
156
+ <class>gene_doddle/observer</class>
157
+ <method>replaceShippingMethodInfo</method>
158
+ </gene_doddle_shipping_method>
159
+ </observers>
160
+ </core_block_abstract_to_html_after>
161
+
162
+ </events>
163
+ </adminhtml>
164
+
165
+ <!-- Basic setup for our shipping method -->
166
+ <default>
167
+ <carriers>
168
+ <gene_doddle>
169
+ <active>0</active>
170
+ <model>gene_doddle/carrier</model>
171
+ <title>Collect from your local train station</title>
172
+
173
+ <name>Doddle</name>
174
+ <more_information><![CDATA[<h3>What is Doddle?</h3><p>Doddle fits in with your life. No more waiting. No more missed deliveries. With Doddle shops in and around train stations you can Doddle (our language for collecting, sending or returning) your parcel on the way to, or from, work, while you’re out shopping or simply whenever you’re passing.</p>]]></more_information>
175
+ <type>I</type>
176
+ <price>5.00</price>
177
+ <collection_lead_time>2</collection_lead_time>
178
+
179
+ <environment>staging</environment>
180
+
181
+ <stores_api><![CDATA[https://api.doddle.it/v1/]]></stores_api>
182
+ <staging_api><![CDATA[https://stage-apigw.doddle.it/v1/]]></staging_api>
183
+ <production_api><![CDATA[https://apigw.doddle.it/v1/]]></production_api>
184
+
185
+ <!-- We force this shipping method to be last to be next to our injected block -->
186
+ <sort_order>9999999999</sort_order>
187
+
188
+ <sallowspecific>0</sallowspecific>
189
+ </gene_doddle>
190
+ </carriers>
191
+ </default>
192
+
193
+ <crontab>
194
+ <jobs>
195
+ <gene_doddle_queue>
196
+ <schedule>
197
+ <cron_expr>*/30 * * * *</cron_expr>
198
+ </schedule>
199
+ <run>
200
+ <model>gene_doddle/observer::runQueue</model>
201
+ </run>
202
+ </gene_doddle_queue>
203
+ </jobs>
204
+ </crontab>
205
+
206
+ </config>
app/code/community/Gene/Doddle/etc/system.xml ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <sections>
4
+ <carriers>
5
+ <groups>
6
+ <gene_doddle translate="label comment">
7
+ <label>Doddle</label>
8
+ <sort_order>2</sort_order>
9
+ <show_in_default>1</show_in_default>
10
+ <show_in_website>0</show_in_website>
11
+ <show_in_store>0</show_in_store>
12
+ <comment><![CDATA[
13
+ <img src="//i.imgur.com/UuuzRzF.png" width="150" style="margin: 4px 0 8px 0;" border="0"/><br />Doddle integration provided by <a href="http://gene.co.uk/" target="_blank">Gene Commerce</a>.
14
+ ]]></comment>
15
+ <fields>
16
+
17
+ <module_version translate="label">
18
+ <label>Magento Module Version</label>
19
+ <frontend_model>gene_doddle/adminhtml_system_config_doddle_moduleversion</frontend_model>
20
+ <sort_order>1</sort_order>
21
+ <show_in_default>1</show_in_default>
22
+ <show_in_website>1</show_in_website>
23
+ <show_in_store>1</show_in_store>
24
+ </module_version>
25
+
26
+ <general translate="label">
27
+ <label>Shipping Methods Settings</label>
28
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
29
+ <sort_order>2</sort_order>
30
+ <show_in_default>1</show_in_default>
31
+ <show_in_website>1</show_in_website>
32
+ <show_in_store>1</show_in_store>
33
+ </general>
34
+
35
+ <active translate="label">
36
+ <label>Enabled</label>
37
+ <frontend_type>select</frontend_type>
38
+ <source_model>adminhtml/system_config_source_yesno</source_model>
39
+ <sort_order>5</sort_order>
40
+ <show_in_default>1</show_in_default>
41
+ <show_in_website>1</show_in_website>
42
+ <show_in_store>0</show_in_store>
43
+ </active>
44
+ <title translate="label">
45
+ <label>Title</label>
46
+ <frontend_type>text</frontend_type>
47
+ <sort_order>10</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>1</show_in_website>
50
+ <show_in_store>1</show_in_store>
51
+ </title>
52
+ <name translate="label">
53
+ <label>Method Name</label>
54
+ <frontend_type>text</frontend_type>
55
+ <sort_order>15</sort_order>
56
+ <show_in_default>1</show_in_default>
57
+ <show_in_website>1</show_in_website>
58
+ <show_in_store>1</show_in_store>
59
+ </name>
60
+ <more_information translate="label">
61
+ <label>More Information</label>
62
+ <frontend_type>textarea</frontend_type>
63
+ <sort_order>20</sort_order>
64
+ <show_in_default>1</show_in_default>
65
+ <show_in_website>1</show_in_website>
66
+ <show_in_store>1</show_in_store>
67
+ <comment><![CDATA[
68
+ This information will be displayed in a tooltip on the billing step and the 'whats this?' link on the shipping methods panel.<br /><strong>Basic HTML allowed</strong>
69
+ ]]></comment>
70
+ </more_information>
71
+
72
+ <price_heading translate="label">
73
+ <label>Pricing</label>
74
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
75
+ <sort_order>25</sort_order>
76
+ <show_in_default>1</show_in_default>
77
+ <show_in_website>1</show_in_website>
78
+ <show_in_store>1</show_in_store>
79
+ </price_heading>
80
+
81
+ <price translate="label comment">
82
+ <label>Price</label>
83
+ <frontend_type>text</frontend_type>
84
+ <sort_order>30</sort_order>
85
+ <show_in_default>1</show_in_default>
86
+ <show_in_website>1</show_in_website>
87
+ <show_in_store>0</show_in_store>
88
+ <comment><![CDATA[
89
+ The total cost you wish to charge to the customer for using Doddle.
90
+ ]]></comment>
91
+ </price>
92
+
93
+ <type translate="label comment">
94
+ <label>Pricing Type</label>
95
+ <frontend_type>select</frontend_type>
96
+ <source_model>adminhtml/system_config_source_shipping_flatrate</source_model>
97
+ <sort_order>31</sort_order>
98
+ <show_in_default>1</show_in_default>
99
+ <show_in_website>1</show_in_website>
100
+ <show_in_store>0</show_in_store>
101
+ <comment><![CDATA[
102
+ Do you wish to charge the customer for each item, or just as a whole order?
103
+ ]]></comment>
104
+ </type>
105
+
106
+ <doddle_settings translate="label">
107
+ <label>Doddle Settings</label>
108
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
109
+ <sort_order>35</sort_order>
110
+ <show_in_default>1</show_in_default>
111
+ <show_in_website>1</show_in_website>
112
+ <show_in_store>1</show_in_store>
113
+ </doddle_settings>
114
+
115
+ <retailer_id translate="label comment">
116
+ <label>Retailer ID</label>
117
+ <frontend_type>text</frontend_type>
118
+ <sort_order>36</sort_order>
119
+ <show_in_default>1</show_in_default>
120
+ <show_in_website>1</show_in_website>
121
+ <show_in_store>0</show_in_store>
122
+ <comment><![CDATA[
123
+ Your Doddle retailer ID, this would have been given to you by your Doddle representative.
124
+ ]]></comment>
125
+ </retailer_id>
126
+
127
+ <environment>
128
+ <label>Environment</label>
129
+ <frontend_type>select</frontend_type>
130
+ <source_model>gene_doddle/system_config_environment</source_model>
131
+ <sort_order>37</sort_order>
132
+ <show_in_default>1</show_in_default>
133
+ <show_in_website>1</show_in_website>
134
+ <show_in_store>1</show_in_store>
135
+ <comment><![CDATA[
136
+ The staging environment allows you to test your integration with Doddle before release.
137
+ ]]></comment>
138
+ </environment>
139
+
140
+ <configure_api>
141
+ <label>Configure API URLs</label>
142
+ <frontend_type>select</frontend_type>
143
+ <source_model>adminhtml/system_config_source_yesno</source_model>
144
+ <sort_order>38</sort_order>
145
+ <show_in_default>1</show_in_default>
146
+ <show_in_website>1</show_in_website>
147
+ <show_in_store>1</show_in_store>
148
+ <comment><![CDATA[
149
+ Enable this if you need to manually update the API endpoints. Invalid configuration of these URLs can stop the Doddle module from functioning.
150
+ ]]></comment>
151
+ </configure_api>
152
+
153
+ <stores_api translate="label comment">
154
+ <label>Store API URL</label>
155
+ <frontend_type>text</frontend_type>
156
+ <sort_order>39</sort_order>
157
+ <show_in_default>1</show_in_default>
158
+ <show_in_website>0</show_in_website>
159
+ <show_in_store>0</show_in_store>
160
+ <comment><![CDATA[
161
+ The URL path for the staging API. (eg. https://api.doddle.it/v1/)
162
+ ]]></comment>
163
+ <depends>
164
+ <configure_api>1</configure_api>
165
+ </depends>
166
+ </stores_api>
167
+
168
+ <staging_api translate="label comment">
169
+ <label>Pre-Advice Staging API URL</label>
170
+ <frontend_type>text</frontend_type>
171
+ <sort_order>40</sort_order>
172
+ <show_in_default>1</show_in_default>
173
+ <show_in_website>0</show_in_website>
174
+ <show_in_store>0</show_in_store>
175
+ <comment><![CDATA[
176
+ The URL path for the staging API. (eg. https://stage-apigw.doddle.it/v1/)
177
+ ]]></comment>
178
+ <depends>
179
+ <configure_api>1</configure_api>
180
+ </depends>
181
+ </staging_api>
182
+
183
+ <production_api translate="label comment">
184
+ <label>Pre-Advice Production API URL</label>
185
+ <frontend_type>text</frontend_type>
186
+ <sort_order>41</sort_order>
187
+ <show_in_default>1</show_in_default>
188
+ <show_in_website>0</show_in_website>
189
+ <show_in_store>0</show_in_store>
190
+ <comment><![CDATA[
191
+ The URL path for the production API. (eg. https://apigw.doddle.it/v1/)
192
+ ]]></comment>
193
+ <depends>
194
+ <configure_api>1</configure_api>
195
+ </depends>
196
+ </production_api>
197
+
198
+ <api_key translate="label comment">
199
+ <label>API Key</label>
200
+ <frontend_type>text</frontend_type>
201
+ <sort_order>42</sort_order>
202
+ <show_in_default>1</show_in_default>
203
+ <show_in_website>1</show_in_website>
204
+ <show_in_store>1</show_in_store>
205
+ <comment><![CDATA[
206
+ The API key for accessing Doddle's API.
207
+ ]]></comment>
208
+ </api_key>
209
+
210
+ <api_secret translate="label comment">
211
+ <label>API Secret</label>
212
+ <frontend_type>text</frontend_type>
213
+ <sort_order>43</sort_order>
214
+ <show_in_default>1</show_in_default>
215
+ <show_in_website>1</show_in_website>
216
+ <show_in_store>1</show_in_store>
217
+ <comment><![CDATA[
218
+ The API secret for accessing Doddle's API.
219
+ ]]></comment>
220
+ </api_secret>
221
+
222
+ <api_scope>
223
+ <label>API Scope</label>
224
+ <frontend_type>text</frontend_type>
225
+ <sort_order>44</sort_order>
226
+ <show_in_default>1</show_in_default>
227
+ <show_in_website>1</show_in_website>
228
+ <show_in_store>1</show_in_store>
229
+ <comment><![CDATA[
230
+ Any additional scope which is needed when communicating with the Doddle API. These should be separated by a space (eg. retailer_Store)
231
+ ]]></comment>
232
+ </api_scope>
233
+
234
+ <collection_lead_time translate="label comment">
235
+ <label>Collection Lead Time</label>
236
+ <frontend_type>text</frontend_type>
237
+ <sort_order>45</sort_order>
238
+ <show_in_default>1</show_in_default>
239
+ <show_in_website>1</show_in_website>
240
+ <show_in_store>0</show_in_store>
241
+ <comment><![CDATA[
242
+ The amount of days it will take for the customers order to be available at their selected Doddle store.
243
+ ]]></comment>
244
+ </collection_lead_time>
245
+
246
+ <google_settings translate="label">
247
+ <label>Google API Settings</label>
248
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
249
+ <sort_order>50</sort_order>
250
+ <show_in_default>1</show_in_default>
251
+ <show_in_website>1</show_in_website>
252
+ <show_in_store>1</show_in_store>
253
+ </google_settings>
254
+
255
+ <google_api_key translate="label comment">
256
+ <label>Google Maps JavaScript API Key</label>
257
+ <frontend_type>text</frontend_type>
258
+ <sort_order>55</sort_order>
259
+ <show_in_default>1</show_in_default>
260
+ <show_in_website>1</show_in_website>
261
+ <show_in_store>0</show_in_store>
262
+ <comment><![CDATA[
263
+ To best determine the users location from their address we require a API key from Google to utilise their Geocoding API's. Please see the API key section of <a href="https://developers.google.com/maps/documentation/geocoding/" target="_blank">this guide</a> for more information.
264
+ ]]></comment>
265
+ </google_api_key>
266
+
267
+ <resitrctions translate="label">
268
+ <label>Restrictions</label>
269
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
270
+ <sort_order>100</sort_order>
271
+ <show_in_default>1</show_in_default>
272
+ <show_in_website>1</show_in_website>
273
+ <show_in_store>1</show_in_store>
274
+ </resitrctions>
275
+
276
+ <sallowspecific translate="label">
277
+ <label>Available for specific countries only</label>
278
+ <frontend_type>select</frontend_type>
279
+ <frontend_class>shipping-applicable-country</frontend_class>
280
+ <source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
281
+ <sort_order>105</sort_order>
282
+ <show_in_default>1</show_in_default>
283
+ <show_in_website>1</show_in_website>
284
+ <show_in_store>0</show_in_store>
285
+ </sallowspecific>
286
+ <specificcountry translate="label">
287
+ <label>Ship to Specific Countries</label>
288
+ <frontend_type>multiselect</frontend_type>
289
+ <sort_order>110</sort_order>
290
+ <source_model>adminhtml/system_config_source_country</source_model>
291
+ <show_in_default>1</show_in_default>
292
+ <show_in_website>1</show_in_website>
293
+ <show_in_store>0</show_in_store>
294
+ <can_be_empty>1</can_be_empty>
295
+ </specificcountry>
296
+ </fields>
297
+ </gene_doddle>
298
+ </groups>
299
+ </carriers>
300
+ </sections>
301
+ </config>
app/code/community/Gene/Doddle/sql/gene_doddle_setup/install-0.1.0.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* @var $installer Mage_Sales_Model_Resource_Setup */
4
+ $installer = $this;
5
+
6
+ $installer->startSetup();
7
+
8
+ // Add our store ID's next to the address fields
9
+ $installer->addAttribute(
10
+ 'quote_address', 'doddle_store_id', array('type' => Varien_Db_Ddl_Table::TYPE_VARCHAR, 'visible' => false)
11
+ );
12
+ $installer->addAttribute(
13
+ 'order_address', 'doddle_store_id', array('type' => Varien_Db_Ddl_Table::TYPE_VARCHAR, 'visible' => false)
14
+ );
15
+
16
+ // Create our API requests table
17
+ $requestTable = $installer->getConnection()->newTable($installer->getTable('gene_doddle/request'))
18
+ ->addColumn('request_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 9, array(
19
+ 'unsigned' => true,
20
+ 'nullable' => false,
21
+ 'primary' => true,
22
+ 'identity' => true,
23
+ 'auto_increment' => true
24
+ ), 'Request ID')
25
+ ->addColumn('order_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 10, array(
26
+ 'unsigned' => true,
27
+ 'nullable' => false
28
+ ), 'Order ID')
29
+ ->addColumn('status', Varien_Db_Ddl_Table::TYPE_VARCHAR, 50, array(
30
+ 'nullable' => true
31
+ ), 'Status of request')
32
+ ->addColumn('message', Varien_Db_Ddl_Table::TYPE_TEXT, false, array(
33
+ 'nullable' => true
34
+ ), 'Any message associated with the failure of the request')
35
+ ->addColumn('preadvice_id', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
36
+ 'nullable' => true
37
+ ), 'Video to play from header area')
38
+ ->addColumn('created_at', Varien_Db_Ddl_Table::TYPE_DATETIME, false, array(
39
+ 'nullable' => true
40
+ ), 'Created at time')
41
+ ->addColumn('updated_at', Varien_Db_Ddl_Table::TYPE_DATETIME, false, array(
42
+ 'nullable' => true
43
+ ), 'Updated at time')
44
+ ->setComment('Doddle API Requests Table');
45
+
46
+ // Actually create the table
47
+ $installer->getConnection()->createTable($requestTable);
48
+
49
+ $installer->endSetup();
app/design/adminhtml/default/default/template/gene/doddle/shippingmethod.phtml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var $this Gene_Doddle_Block_Adminhtml_Sales_Order_View_Shippingmethod */ ?>
2
+ <?php $_order = $this->getOrder() ?>
3
+
4
+ <?php if ($_order->getTracksCollection()->count()) : ?>
5
+ <a href="#" id="linkId" onclick="popWin('<?php echo $this->helper('shipping')->getTrackingPopupUrlBySalesModel($_order) ?>','trackorder','width=800,height=600,resizable=yes,scrollbars=yes')" title="<?php echo $this->__('Track Order') ?>"><?php echo $this->__('Track Order') ?></a>
6
+ <br/>
7
+ <?php endif; ?>
8
+ <?php if ($_order->getShippingDescription()): ?>
9
+
10
+ <!-- Include our Doddle logo -->
11
+ <img src="https://i.imgur.com/UuuzRzF.png" width="120" style="margin: 2px 0 8px 0;" border="0"/><br />
12
+ <strong><?php echo $this->__('Collection from: %s', $_order->getShippingAddress()->getName()); ?></strong>
13
+
14
+ <?php if ($this->helper('tax')->displayShippingPriceIncludingTax()): ?>
15
+ <?php $_excl = $this->displayShippingPriceInclTax($_order); ?>
16
+ <?php else: ?>
17
+ <?php $_excl = $this->displayPriceAttribute('shipping_amount', false, ' '); ?>
18
+ <?php endif; ?>
19
+ <?php $_incl = $this->displayShippingPriceInclTax($_order); ?>
20
+
21
+ <?php echo $_excl; ?>
22
+ <?php if ($this->helper('tax')->displayShippingBothPrices() && $_incl != $_excl): ?>
23
+ (<?php echo $this->__('Incl. Tax'); ?> <?php echo $_incl; ?>)
24
+ <?php endif; ?>
25
+
26
+ <?php if($preAdvices = $this->getPreAdvices()): ?>
27
+ <?php foreach($preAdvices as $preAdvice): ?>
28
+ <br /><strong><?php echo $this->__('Pre-advice ID:'); ?></strong> <?php echo $preAdvice->getPreadviceId(); ?>
29
+ <?php endforeach; ?>
30
+ <?php endif; ?>
31
+
32
+ <?php else: ?>
33
+ <?php echo $this->helper('sales')->__('No shipping information available'); ?>
34
+ <?php endif; ?>
app/design/frontend/base/default/layout/gene/doddle.xml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+
4
+ <checkout_onepage_index>
5
+ <reference name="head">
6
+ <action method="addJs">
7
+ <file>gene/doddle/doddle.js</file>
8
+ </action>
9
+ <action method="addItem"><type>skin_css</type><file>css/gene/doddle/doddle.css</file></action>
10
+ </reference>
11
+ <reference name="before_body_end">
12
+ <block type="gene_doddle/onepage_setup" name="gene.doddle.setup"
13
+ template="gene/doddle/onepage/setup.phtml"/>
14
+ </reference>
15
+ </checkout_onepage_index>
16
+
17
+ <doddle_index_getclosestlonglat>
18
+
19
+ <remove name="right"/>
20
+ <remove name="left"/>
21
+ <remove name="footer"/>
22
+
23
+ <block type="gene_doddle/onepage_shipping_method_doddle_table" name="root" output="toHtml"
24
+ template="gene/doddle/onepage/shipping/method/doddle/table.phtml" />
25
+
26
+ </doddle_index_getclosestlonglat>
27
+
28
+ </layout>
app/design/frontend/base/default/template/gene/doddle/onepage/billing/option.phtml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <li class="control doddle-control">
2
+
3
+ <?php if($moreInformation = $this->getMoreInformation()): ?>
4
+ <div class="doddle-billing-more-information">
5
+ <?php echo $moreInformation; ?>
6
+ </div>
7
+ <?php endif; ?>
8
+
9
+ <input type="radio" name="billing[use_for_shipping]" id="billing:use_for_doddle" value="doddle" title="Collect From Doddle" class="radio" />
10
+ <label for="billing:use_for_doddle"><span class="ship-to"><?php echo $this->__('Collect From'); ?></span> <img src="<?php echo $this->getSkinUrl('images/gene/doddle/logo.png'); ?>" class="doddle-logo" /></label>
11
+ </li>
app/design/frontend/base/default/template/gene/doddle/onepage/progress/shipping_method.phtml ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ($this->getCheckout()->getStepData('shipping_method', 'complete')): ?>
2
+ <dt class="complete">
3
+ <?php echo $this->__('Shipping Method') ?> <span class="changelink"><span class="separator">|</span> <a
4
+ href="#shipping_method"
5
+ onclick="checkout.changeSection('opc-shipping_method'); return false;"><?php echo $this->__('Change') ?></a></span>
6
+ </dt>
7
+ <dd class="complete">
8
+ <?php if ($this->getShippingMethod()): ?>
9
+
10
+ <!-- Include our Doddle logo -->
11
+ <img src="<?php echo $this->getSkinUrl('images/gene/doddle/logo.png'); ?>" class="doddle-sidebar-logo" border="0"/>
12
+ <p><?php echo $this->__('Collection from: %s', Mage::getSingleton('checkout/session')->getDoddleStoreName()); ?></p>
13
+
14
+ <?php $_excl = $this->getShippingPriceExclTax(); ?>
15
+ <?php $_incl = $this->getShippingPriceInclTax(); ?>
16
+ <?php if(floatval(preg_replace("/([^0-9\\.])/i", "", $_incl)) == 0): ?>
17
+ <strong><?php echo $this->__('FREE'); ?></strong>
18
+ <?php else: ?>
19
+ <?php if ($this->helper('tax')->displayShippingPriceIncludingTax()): ?>
20
+ <?php echo $_incl; ?>
21
+ <?php else: ?>
22
+ <?php echo $_excl; ?>
23
+ <?php endif; ?>
24
+ <?php if ($this->helper('tax')->displayShippingBothPrices() && $_incl != $_excl): ?>
25
+ (<?php echo $this->__('Incl. Tax'); ?> <?php echo $_incl; ?>)
26
+ <?php endif; ?>
27
+ <?php endif; ?>
28
+
29
+ <?php else: ?>
30
+ <?php echo $this->__('Shipping method has not been selected yet') ?>
31
+ <?php endif; ?>
32
+ </dd>
33
+ <?php else: ?>
34
+ <dt>
35
+ <?php echo $this->__('Shipping Method') ?>
36
+ </dt>
37
+ <?php endif; ?>
38
+
app/design/frontend/base/default/template/gene/doddle/onepage/setup.phtml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if($googleApiKey = $this->getGoogleApiKey()): ?>
2
+ <script type="text/javascript"
3
+ src="https://maps.googleapis.com/maps/api/js?key=<?php echo $googleApiKey; ?>">
4
+ </script>
5
+ <?php endif; ?>
6
+
7
+ <script type="text/javascript">
8
+ var doddle = new Doddle(
9
+ '<?php echo $this->getDoddleShippingMethodValue(); ?>',
10
+ '<?php echo Mage::getUrl('doddle/index/getClosestLongLat', array('_secure' => true)); ?>'
11
+ );
12
+ </script>
app/design/frontend/base/default/template/gene/doddle/onepage/shipping/method/doddle.phtml ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $this Gene_Doddle_Block_Onepage_Shipping_Method_Doddle */
3
+ ?>
4
+ <div id="doddle-box"<?php echo (!$this->onlyDoddle() ? ' style="display: none;"' : ''); ?>>
5
+
6
+ <?php if($this->onlyDoddle()): ?>
7
+ <div style="display: none;">
8
+ <input name="shipping_method" type="radio" value="<?php echo Mage::helper('gene_doddle')->getShippingMethodCode(); ?>" id="s_method_<?php echo Mage::helper('gene_doddle')->getShippingMethodCode(); ?>" checked="checked" class="radio" />
9
+ </div>
10
+ <?php endif; ?>
11
+
12
+ <div class="doddle-header">
13
+ <img src="<?php echo $this->getSkinUrl('images/gene/doddle/logo.png'); ?>" class="doddle-logo" />
14
+
15
+ <?php if($moreInformation = $this->getMoreInformation()): ?>
16
+ <a href="#" class="whats-this"><?php echo $this->__('what\'s this?'); ?></a>
17
+ <div id="doddle-more-information" style="display: none;">
18
+ <?php echo $moreInformation; ?>
19
+ </div>
20
+ <?php endif; ?>
21
+ </div>
22
+
23
+ <h3>
24
+ <?php echo $this->__('Find your nearest Doddle collection point'); ?>
25
+ <?php if($this->getPrice() == 0): ?>
26
+ <?php echo $this->__(' - <strong>FREE</strong>'); ?>
27
+ <?php else: ?>
28
+ <?php echo $this->__(' - Only %s', '<strong>' . $this->getFormattedPrice() . '</strong>'); ?>
29
+ <?php endif; ?>
30
+ </h3>
31
+
32
+ <ul class="messages" style="display: none;">
33
+ <li class="error-msg">
34
+ <ul>
35
+
36
+ </ul>
37
+ </li>
38
+ </ul>
39
+
40
+ <div class="doddle-search">
41
+ <button type="button" class="button doddle-search-button" onclick="return doddle.getCurrentPosition(this);"><span><span><?php echo $this->__('Use my current location'); ?></span></span></button>
42
+ <span class="doddle-or"><?php echo $this->__('or'); ?></span>
43
+ <div class="doddle-search-form">
44
+ <input type="text" name="search" class="doddle-search-input" id="doddle-search-input" placeholder="Enter city or postcode" />
45
+ <button type="button" class="button doddle-search-button" id="doddle-search-button" onclick="return doddle.getPositionFromAddress($('doddle-search-input').value, this);"><span><span><?php echo $this->__('Search'); ?></span></span></button>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="hidden-validation">
50
+ <input type="radio" name="doddle-store" id="store-empty" value="" class="validate-doddle" />
51
+ </div>
52
+
53
+ <div id="doddle-table">
54
+ <!-- This is populated via an Ajax request -->
55
+ </div>
56
+ </div>
57
+
58
+ <script type="text/javascript">
59
+ // Observe shipping method choices
60
+ doddle.observeShippingMethods();
61
+
62
+ // Stop hitting submit taking you to the next step
63
+ doddle.handleSubmit();
64
+
65
+ <?php if($this->onlyDoddle()): ?>
66
+ if($('shipping-progress-opcheckout') != undefined) {
67
+ $('shipping-progress-opcheckout').hide();
68
+ }
69
+ <?php endif; ?>
70
+
71
+ // Hover on whats this
72
+ if($$('.whats-this').first() != undefined) {
73
+
74
+ // Assign our variable
75
+ var whatsThisTimeout;
76
+
77
+ // Observe the mouse over
78
+ Element.observe($$('.whats-this').first(), 'mouseover', function() {
79
+ clearTimeout(whatsThisTimeout);
80
+ $('doddle-more-information').show();
81
+ });
82
+
83
+ // Check mouseout
84
+ Element.observe($$('.whats-this').first(), 'mouseout', function() {
85
+ whatsThisTimeout = setTimeout(function() {
86
+ $('doddle-more-information').hide();
87
+ }, 250);
88
+ });
89
+
90
+ // Observe the mouse over
91
+ Element.observe('doddle-more-information', 'mouseover', function() {
92
+ clearTimeout(whatsThisTimeout);
93
+ $('doddle-more-information').show();
94
+ });
95
+
96
+ Element.observe('doddle-more-information', 'mouseout', function() {
97
+ whatsThisTimeout = setTimeout(function() {
98
+ $('doddle-more-information').hide();
99
+ }, 250);
100
+ });
101
+
102
+ }
103
+
104
+ // Add our new validation type
105
+ Validation.add('validate-doddle', '<?php echo $this->__('Please select your Doddle collection store to continue.'); ?>', function(v) {
106
+
107
+ // Check to see whether a method has been selected
108
+ if($$('[name=doddle-store]:checked').first() == undefined) {
109
+ doddle.throwError('<?php echo $this->__('Please select your Doddle collection store to continue.'); ?>');
110
+ return false;
111
+ }
112
+
113
+ return true;
114
+ });
115
+ </script>
app/design/frontend/base/default/template/gene/doddle/onepage/shipping/method/doddle/table.phtml ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $this Gene_Doddle_Block_Onepage_Shipping_Method_Doddle_Table */
3
+ ?>
4
+ <?php if($this->getStores()): ?>
5
+ <table id="doddle-stores" width="100%" cellspacing="0">
6
+ <thead>
7
+ <tr>
8
+ <th colspan="2"><?php echo $this->__('Store Address'); ?></th>
9
+ <th><?php echo $this->__('Dist.'); ?></th>
10
+ <th><?php echo $this->__('Collection Date'); ?></th>
11
+ <th></th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <?php
16
+ /* @var $store Gene_Doddle_Model_Store */
17
+ foreach($this->getStores() as $store): ?>
18
+ <tr>
19
+ <td class="input"><input type="radio" name="doddle-store" id="store-<?php echo $store->getId(); ?>" value="<?php echo $store->getId(); ?>" class="validate-doddle" /></td>
20
+ <td width="45%">
21
+ <label for="store-<?php echo $store->getId(); ?>"><?php echo $store->getName(); ?></label>
22
+ <?php if($address = $store->getAddress()): ?>
23
+ <address>
24
+ <label for="store-<?php echo $store->getId(); ?>"><?php echo $address; ?></label>
25
+ </address>
26
+ <?php endif; ?>
27
+ </td>
28
+ <td width="15%"><?php echo $store->getDistance(); ?> mi</td>
29
+ <td width="32%"><?php echo $this->getCollectionDate(); ?></td>
30
+ <td width="8%" class="more-information-td">
31
+ <a href="#" title="More Information" class="doddle-info">i</a>
32
+ <div class="more-information">
33
+ <?php if($openingTimes = $store->getOpeningTimes()): ?>
34
+ <div class="opening-times">
35
+ <h3><?php echo $this->__('Opening Times:'); ?></h3>
36
+ <table width="100%" class="opening-times-table">
37
+ <?php foreach($openingTimes as $openingTime): ?>
38
+ <tr>
39
+ <th><?php echo $openingTime['label']; ?></th>
40
+ <td>
41
+ <?php if($openingTime['value'] === false): ?>
42
+ <strong><?php echo $this->__('CLOSED'); ?></strong>
43
+ <?php else: ?>
44
+ <?php echo $openingTime['value']; ?>
45
+ <?php endif; ?>
46
+ </td>
47
+ </tr>
48
+ <?php endforeach; ?>
49
+ </table>
50
+ </div>
51
+ <?php endif; ?>
52
+ <div class="doddle-map">
53
+ <iframe width="100%" height="100%" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://maps.google.com/maps?q=<?php echo $store->getLat(); ?>,<?php echo $store->getLong(); ?>&z=14&output=embed"></iframe>
54
+ </div>
55
+ </div>
56
+ </td>
57
+ </tr>
58
+ <?php endforeach; ?>
59
+ </tbody>
60
+ </table>
61
+ <?php else: ?>
62
+ <ul class="messages">
63
+ <li class="error-msg">
64
+ <ul>
65
+ <li><?php echo $this->__('Our Doddle service is currently unavailable, please try using an alternative shipping method.'); ?></li>
66
+ </ul>
67
+ </li>
68
+ </ul>
69
+ <?php endif; ?>
app/etc/modules/Gene_Doddle.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Gene_Doddle>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </Gene_Doddle>
8
+ </modules>
9
+ </config>
js/gene/doddle/doddle.js ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /**
3
+ * Doddle Class for Magento One Page Checkout
4
+ *
5
+ * @class Doddle
6
+ * @author Dave Macaulay <dave@gene.co.uk>
7
+ */
8
+ var Doddle = Class.create();
9
+ Doddle.prototype = {
10
+
11
+ /**
12
+ * Set some values in the class when we initialize
13
+ *
14
+ * @param shippingMethodValue The shipping method code/value
15
+ * @param latLongAjaxUrl The ajax URL for requesting stores via lat & long
16
+ */
17
+ initialize: function (shippingMethodValue, latLongAjaxUrl) {
18
+
19
+ this.shippingMethodValue = shippingMethodValue;
20
+ this.latLongAjaxUrl = latLongAjaxUrl;
21
+
22
+ this.storeSelection = false;
23
+ },
24
+
25
+ /**
26
+ * Watch for the user changing the shipping method
27
+ */
28
+ observeShippingMethods: function() {
29
+
30
+ // Loop through each radio and watch for changes
31
+ $$('[name="shipping_method"]').each(function(element) {
32
+ Element.observe(element, 'change', function(event) {
33
+ if(Event.findElement(event).value == this.shippingMethodValue) {
34
+
35
+ // Hide the shipping address in the progress area
36
+ if($('shipping-progress-opcheckout') != undefined) {
37
+ $('shipping-progress-opcheckout').hide();
38
+ }
39
+
40
+ $('doddle-box').show();
41
+
42
+ } else {
43
+
44
+ // Show the shipping address in the progress area
45
+ if($('shipping-progress-opcheckout') != undefined) {
46
+ $('shipping-progress-opcheckout').show();
47
+ }
48
+
49
+ $('doddle-box').hide();
50
+
51
+ }
52
+ }.bind(this));
53
+ }.bind(this));
54
+
55
+ // Check to see if Doddle is selected when the step loads
56
+ if($$('[name="shipping_method"]:checked').first() != undefined) {
57
+
58
+ // Determine which method is selected
59
+ if($$('[name="shipping_method"]:checked').first().value == this.shippingMethodValue) {
60
+
61
+ // Hide the shipping address in the progress area
62
+ if($('shipping-progress-opcheckout') != undefined) {
63
+ $('shipping-progress-opcheckout').hide();
64
+ }
65
+
66
+ $('doddle-box').show();
67
+
68
+ } else {
69
+
70
+ // Show the shipping address in the progress area
71
+ if($('shipping-progress-opcheckout') != undefined) {
72
+ $('shipping-progress-opcheckout').show();
73
+ }
74
+
75
+ $('doddle-box').hide();
76
+
77
+ }
78
+
79
+ }
80
+
81
+ },
82
+
83
+ /**
84
+ * If the user hits submit within the search box submit our action instead of the checkouts action
85
+ */
86
+ handleSubmit: function() {
87
+
88
+ // If the user presses enter within the input we return false
89
+ $('doddle-search-input').observe('keypress', function(e) {
90
+
91
+ // Look for keyCode 13 (which is enter)
92
+ if(e.keyCode == 13) {
93
+
94
+ // Make the request
95
+ this.getPositionFromAddress($('doddle-search-input').value, $('doddle-search-button'));
96
+
97
+ if (e.preventDefault) {
98
+ e.preventDefault();
99
+ }
100
+ return false;
101
+ }
102
+ }.bind(this));
103
+
104
+ },
105
+
106
+ /**
107
+ * Get the users current position from GeoLocation
108
+ *
109
+ * @param buttonElement
110
+ */
111
+ getCurrentPosition: function(buttonElement) {
112
+
113
+ // Add the loading class name to the button
114
+ if(buttonElement) {
115
+ $(buttonElement).addClassName('loading').setAttribute('disabled', 'disabled');
116
+ }
117
+
118
+ // Do a call to the browsers GeoLocation API
119
+ navigator.geolocation.getCurrentPosition(
120
+ function(position) {
121
+
122
+ // Update the stores table
123
+ this.findClosestStores(position.coords.latitude, position.coords.longitude, function() {
124
+
125
+ // Remove the loading class
126
+ if(buttonElement) {
127
+ $(buttonElement).removeClassName('loading').removeAttribute('disabled');
128
+ }
129
+
130
+ });
131
+
132
+ }.bind(this),
133
+ function(error) {
134
+
135
+ // Aim the message towards the user
136
+ switch (error.code) {
137
+ case error.PERMISSION_DENIED:
138
+ this.throwError('You have to agree for us to use your location to use this feature.');
139
+ break;
140
+ default:
141
+ this.throwError('We\'re unable to automatically retrieve your location.');
142
+ break;
143
+ }
144
+
145
+ // Remove the loading class
146
+ if(buttonElement) {
147
+ $(buttonElement).removeClassName('loading').removeAttribute('disabled');
148
+ }
149
+
150
+ }.bind(this)
151
+ );
152
+
153
+ },
154
+
155
+ /**
156
+ * Get the position from an address and find the closest stores
157
+ *
158
+ * @param address
159
+ * @param buttonElement
160
+ */
161
+ getPositionFromAddress: function(address, buttonElement) {
162
+
163
+ // Check to see if the address is empty
164
+ if(address == '') {
165
+ return false;
166
+ }
167
+
168
+ // Add the loading class name to the button
169
+ if(buttonElement) {
170
+ $(buttonElement).addClassName('loading').setAttribute('disabled', 'disabled');
171
+ }
172
+
173
+ // Start a new instance of the Geocoder
174
+ geocoder = new google.maps.Geocoder();
175
+
176
+ // Make the request to the API
177
+ geocoder.geocode({
178
+ address: address
179
+ }, function(results, status) {
180
+
181
+ if(status == google.maps.GeocoderStatus.OK) {
182
+
183
+ // Grab the geo coding information from the model
184
+ geoLocation = results.first().geometry.location;
185
+
186
+ // Update the stores table
187
+ this.findClosestStores(geoLocation.lat(), geoLocation.lng(), function() {
188
+
189
+ // Remove the loading class
190
+ if(buttonElement) {
191
+ $(buttonElement).removeClassName('loading').removeAttribute('disabled');
192
+ }
193
+
194
+ });
195
+ }
196
+
197
+ }.bind(this));
198
+
199
+ },
200
+
201
+ /**
202
+ * Find the closest stores user lat & long
203
+ *
204
+ * @param lat
205
+ * @param long
206
+ * @param callback
207
+ */
208
+ findClosestStores: function(lat, long, callback) {
209
+
210
+ this.captureUserSelection();
211
+
212
+ // Make a new ajax request to the server
213
+ new Ajax.Request(
214
+ this.latLongAjaxUrl,
215
+ {
216
+ method: 'get',
217
+ onSuccess: function(transport) {
218
+
219
+ // Verify we have some response text
220
+ if (transport && transport.responseJSON) {
221
+
222
+ // If we have JSON use it
223
+ response = transport.responseJSON;
224
+
225
+ } else if(transport && transport.responseText) {
226
+
227
+ // Parse as an object
228
+ try {
229
+ response = eval('(' + transport.responseText + ')');
230
+ }
231
+ catch (e) {
232
+ response = {};
233
+ }
234
+
235
+ }
236
+
237
+ // If the request was successful let's update!
238
+ if(response.success && response.html) {
239
+
240
+ // Update the DOM with our table
241
+ $('doddle-table').innerHTML = response.html;
242
+
243
+ // Force the height of the map to the height of the tables
244
+ $$('#doddle-table .doddle-map').each(function (e) {
245
+ if(e.previous('.opening-times') != undefined) {
246
+ e.setStyle({height: e.previous('.opening-times').getHeight() + 'px'});
247
+ }
248
+ });
249
+
250
+ } else if(response.error) {
251
+
252
+ // Attempt to throw the error nicely
253
+ this.throwError(response.error);
254
+ }
255
+
256
+ // If there is a callback function defined call it
257
+ if(callback) {
258
+ callback();
259
+ }
260
+
261
+ // Restore any selection the user has made
262
+ this.restoreUserSelection();
263
+
264
+ }.bind(this),
265
+ parameters: {'lat': lat,'long': long}
266
+ }
267
+ );
268
+
269
+ },
270
+
271
+ /**
272
+ * Capture the current users selection to be restored after an operation
273
+ */
274
+ captureUserSelection: function() {
275
+
276
+ // Always set back to false
277
+ this.storeSelection = false;
278
+
279
+ // Check that one of the radio buttons is checked
280
+ if($$('[name=doddle-store]:checked').first() != undefined) {
281
+ this.storeSelection = $$('[name=doddle-store]:checked').first().value;
282
+ }
283
+
284
+ },
285
+
286
+ /**
287
+ * Restore the users previous selection
288
+ */
289
+ restoreUserSelection: function() {
290
+
291
+ // Check there is a previously store selection
292
+ if(this.storeSelection) {
293
+
294
+ // Check that the radio button still exists
295
+ if($('store-' + this.storeSelection) != undefined) {
296
+ $('store-' + this.storeSelection).checked = true;
297
+ }
298
+
299
+ // Unset the store selection
300
+ this.storeSelection = false;
301
+
302
+ }
303
+
304
+ },
305
+
306
+ /**
307
+ * Throw an error, attempt to make it pretty
308
+ *
309
+ * @param message
310
+ */
311
+ throwError: function(message) {
312
+
313
+ // Verify the message DOM exists
314
+ if($$('#doddle-box ul.messages').first() != undefined) {
315
+
316
+ // Pull out the message box
317
+ messageBox = $$('#doddle-box ul.messages').first();
318
+
319
+ // Build our new mark up
320
+ errorElement = new Element('li').update(new Element('span').update(message));
321
+
322
+ // Retrieve the UL
323
+ ul = messageBox.down('ul');
324
+
325
+ // Remove any previous errors
326
+ if(ul.down('li') != undefined) {
327
+ ul.down('li').remove();
328
+ }
329
+
330
+ // Add in the message
331
+ ul.insert(errorElement);
332
+
333
+ // Show the message box
334
+ messageBox.show();
335
+
336
+ // Hide the message box after 7.5 seconds
337
+ hideMessages = false;
338
+ clearTimeout(hideMessages);
339
+ hideMessages = setTimeout(function () {
340
+ messageBox.hide();
341
+ }, 7500);
342
+
343
+ } else {
344
+ alert(message);
345
+ }
346
+
347
+ }
348
+
349
+ };
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Gene_Doddle</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/mit-license.php">MIT License</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Love shopping online but hate missing the delivery? Fix this for your customers with Doddle, the solution for collecting and returning your online purchases.</summary>
10
+ <description>Love shopping online but hate missing the delivery? Fix this for your customers with Doddle, the solution for collecting and returning your online purchases. With a growing network of stores (www.doddle.it/locations) near train stations, Doddle is open early until late, seven days a week, so you can click and collect your parcels at a time that suits you, whether it&#x2019;s on the way home from work, on your break or after the gym. Quit queuing and never miss a parcel again! To find out more, visit the Doddle website. www.doddle.it</description>
11
+ <notes>Doddle will give your customers a parcel pick up and drop off service that fits with their busy lives. </notes>
12
+ <authors><author><name>Dave Macaulay</name><user>dave</user><email>magento@gene.co.uk</email></author></authors>
13
+ <date>2015-06-19</date>
14
+ <time>11:10:47</time>
15
+ <contents><target name="magecommunity"><dir name="Gene"><dir name="Doddle"><dir name="Block"><dir name="Adminhtml"><dir name="Sales"><dir name="Order"><dir name="View"><file name="Shippingmethod.php" hash="d6315db75e4a2b34f1365130d79b0c47"/></dir></dir></dir><dir name="System"><dir name="Config"><dir name="Doddle"><file name="Moduleversion.php" hash="c5c694a23356e9cb6120c36b39929a95"/></dir></dir></dir></dir><dir name="Onepage"><dir name="Billing"><file name="Option.php" hash="06c044f97c3604805cda9c6a5f727885"/></dir><file name="Setup.php" hash="5efd1f0afc74291dfd9b18c4e2a6adf5"/><dir name="Shipping"><dir name="Method"><dir name="Doddle"><file name="Table.php" hash="76f5dab3e3fd7f7f65ba0ebe521e5a64"/></dir><file name="Doddle.php" hash="0c2e9524d7fbfbb268e1ee974720ddc6"/></dir></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="b7af991bf5b6415b0079fedb1c946cfb"/></dir><dir name="Model"><dir name="Api"><dir name="Doddle"><file name="Abstract.php" hash="8b228e9ccb43ea985a88bbfd35ba1716"/><file name="Preadvice.php" hash="b59b09c0856a6faf1ec48532cf2c3670"/><file name="Stores.php" hash="973e80f3f81e8a1a73306bf9b76e8cfc"/></dir></dir><file name="Carrier.php" hash="65192881262286e5f83cb0f92d86219f"/><file name="Observer.php" hash="fbb1a94ff27516a967c5827c127c1035"/><file name="Request.php" hash="d3721ffea66c0b344ceea7d01bb4f232"/><dir name="Resource"><dir name="Request"><file name="Collection.php" hash="a907852a15314e5cfb978ae4b7b8c05b"/></dir><file name="Request.php" hash="f6826850311ea198ec4a5a1cab3cf811"/></dir><file name="Store.php" hash="97695d1817e3d2d854ed28e16b305163"/><dir name="System"><dir name="Config"><file name="Environment.php" hash="a95a2d409db81660d3107a2ee99767fc"/></dir></dir></dir><dir name="controllers"><file name="IndexController.php" hash="eb1faa127215653de649309a4e21abcd"/></dir><dir name="etc"><file name="config.xml" hash="5d691cd84d80809dd281e4666edd3778"/><file name="system.xml" hash="37e0bb20205e8e8bafa24dc809f6033d"/></dir><dir name="sql"><dir name="gene_doddle_setup"><file name="install-0.1.0.php" hash="73c683d2ae93bc2ea7f0c283ebd4ce3b"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><dir name="gene"><file name="doddle.xml" hash="ca6f41b718803e46606cf4952d3f86be"/></dir></dir><dir name="template"><dir name="gene"><dir name="doddle"><dir name="onepage"><dir name="billing"><file name="option.phtml" hash="645b8e40a9313a70b0134fa19e13506d"/></dir><dir name="progress"><file name="shipping_method.phtml" hash="5dd9f297797876f2dc427cce80b8e791"/></dir><file name="setup.phtml" hash="6e26929e45abe2475dc25bc7b9664bed"/><dir name="shipping"><dir name="method"><dir name="doddle"><file name="table.phtml" hash="95b8cf799bb441724b5af26bb28c5ba0"/></dir><file name="doddle.phtml" hash="54cdb6b598c01bc58f08dccae2779a6f"/></dir></dir></dir></dir></dir></dir></dir></dir></dir><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="gene"><dir name="doddle"><file name="shippingmethod.phtml" hash="49babe1d84ce72f694f571e031d19ed9"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Gene_Doddle.xml" hash="35e36d9baa255ff3595c49c025a0b7c7"/></dir></target><target name="mageweb"><dir name="js"><dir name="gene"><dir name="doddle"><file name="doddle.js" hash="d86ab1d2fce00c9189c3860f890d1bc3"/></dir></dir></dir></target><target name="mageskin"><dir name="frontend"><dir name="rwd"><dir name="default"><dir name="css"><dir name="gene"><dir name="doddle"><file name="config.codekit" hash="2531ed4f72686a8889d2438a057f967a"/><file name="doddle.css" hash="d1963811fcf58d70ba9556d4e7a9915d"/><file name="doddle.less" hash="98f7c7d09e0d96017eb806dd9a14cfc4"/><dir name="fonts"><file name="doddle-icon.eot" hash="01525b5cf95c9efeba43a2280e2300d6"/><file name="doddle-icon.svg" hash="678a5b78314e464b3879809c64a1ef9f"/><file name="doddle-icon.ttf" hash="37378fd364e51d6c09540d56e9981c86"/><file name="doddle-icon.woff" hash="a2e9fc0d7a841e5b99d12ea9b4bc73b5"/></dir></dir></dir></dir></dir></dir><dir name="base"><dir name="default"><dir name="images"><dir name="gene"><dir name="doddle"><file name="logo.png" hash="4ec4e3666cd7f147beebfcb0b9de0d33"/><file name="opc-ajax-loader.gif" hash="f48ee069890b16455c3ddcacee9b5f75"/></dir></dir></dir><dir name="css"><dir name="gene"><dir name="doddle"><file name="config.codekit" hash="2531ed4f72686a8889d2438a057f967a"/><file name="doddle.css" hash="d1963811fcf58d70ba9556d4e7a9915d"/><file name="doddle.less" hash="028ca1a117823ab56c8f3d592fe9ef9b"/><dir name="fonts"><file name="doddle-icon.eot" hash="01525b5cf95c9efeba43a2280e2300d6"/><file name="doddle-icon.svg" hash="678a5b78314e464b3879809c64a1ef9f"/><file name="doddle-icon.ttf" hash="37378fd364e51d6c09540d56e9981c86"/><file name="doddle-icon.woff" hash="a2e9fc0d7a841e5b99d12ea9b4bc73b5"/></dir></dir></dir></dir></dir></dir></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.2.1</min><max>6.0.0</max></php></required></dependencies>
18
+ </package>
skin/frontend/base/default/css/gene/doddle/config.codekit ADDED
@@ -0,0 +1,805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "CodeKitInfo": "This is a CodeKit 2.x project configuration file. It is designed to sync project settings across multiple machines. MODIFYING THE CONTENTS OF THIS FILE IS A POOR LIFE DECISION. If you do so, you will likely cause CodeKit to crash. This file is not useful unless accompanied by the project that created it in CodeKit 2. This file is not backwards-compatible with CodeKit 1.x. For more information, see: http:\/\/incident57.com\/codekit",
3
+ "creatorBuild": "18270",
4
+ "files": {
5
+ "\/doddle.less": {
6
+ "allowInsecureImports": 0,
7
+ "createSourceMap": 0,
8
+ "disableJavascript": 0,
9
+ "fileType": 1,
10
+ "ieCompatibility": 1,
11
+ "ignore": 0,
12
+ "ignoreWasSetByUser": 0,
13
+ "inputAbbreviatedPath": "\/doddle.less",
14
+ "outputAbbreviatedPath": "\/doddle.css",
15
+ "outputPathIsOutsideProject": 0,
16
+ "outputPathIsSetByUser": 1,
17
+ "outputStyle": 0,
18
+ "relativeURLS": 0,
19
+ "shouldRunAutoprefixer": 0,
20
+ "shouldRunBless": 0,
21
+ "strictImports": 0,
22
+ "strictMath": 0,
23
+ "strictUnits": 0
24
+ }
25
+ },
26
+ "hooks": [
27
+ ],
28
+ "lastSavedByUser": "David Macaulay",
29
+ "manualImportLinks": {
30
+ },
31
+ "projectAttributes": {
32
+ "bowerAbbreviatedPath": "",
33
+ "displayValue": "doddle",
34
+ "displayValueWasSetByUser": 0,
35
+ "iconImageName": "brackets_gray"
36
+ },
37
+ "projectSettings": {
38
+ "alwaysUseExternalServer": 0,
39
+ "animateCSSInjections": 1,
40
+ "autoApplyPSLanguageSettingsStyle": 0,
41
+ "autoprefixerBrowserString": "> 1%, last 2 versions, Firefox ESR, Opera 12.1",
42
+ "autoSyncProjectSettingsFile": 1,
43
+ "browserRefreshDelay": 0,
44
+ "coffeeAutoOutputPathEnabled": 1,
45
+ "coffeeAutoOutputPathFilenamePattern": "*.js",
46
+ "coffeeAutoOutputPathRelativePath": "",
47
+ "coffeeAutoOutputPathReplace1": "",
48
+ "coffeeAutoOutputPathReplace2": "",
49
+ "coffeeAutoOutputPathStyle": 0,
50
+ "coffeeCreateSourceMap": 0,
51
+ "coffeeLintFlags2": {
52
+ "arrow_spacing": {
53
+ "active": 0,
54
+ "flagValue": -1
55
+ },
56
+ "camel_case_classes": {
57
+ "active": 1,
58
+ "flagValue": -1
59
+ },
60
+ "colon_assignment_spacing": {
61
+ "active": 0,
62
+ "flagValue": 1
63
+ },
64
+ "cyclomatic_complexity": {
65
+ "active": 0,
66
+ "flagValue": 10
67
+ },
68
+ "duplicate_key": {
69
+ "active": 1,
70
+ "flagValue": -1
71
+ },
72
+ "empty_constructor_needs_parens": {
73
+ "active": 0,
74
+ "flagValue": -1
75
+ },
76
+ "indentation": {
77
+ "active": 1,
78
+ "flagValue": 2
79
+ },
80
+ "line_endings": {
81
+ "active": 0,
82
+ "flagValue": 0
83
+ },
84
+ "max_line_length": {
85
+ "active": 0,
86
+ "flagValue": 150
87
+ },
88
+ "missing_fat_arrows": {
89
+ "active": 0,
90
+ "flagValue": -1
91
+ },
92
+ "newlines_after_classes": {
93
+ "active": 0,
94
+ "flagValue": 3
95
+ },
96
+ "no_backticks": {
97
+ "active": 1,
98
+ "flagValue": -1
99
+ },
100
+ "no_debugger": {
101
+ "active": 1,
102
+ "flagValue": -1
103
+ },
104
+ "no_empty_functions": {
105
+ "active": 0,
106
+ "flagValue": -1
107
+ },
108
+ "no_empty_param_list": {
109
+ "active": 0,
110
+ "flagValue": -1
111
+ },
112
+ "no_implicit_braces": {
113
+ "active": 1,
114
+ "flagValue": -1
115
+ },
116
+ "no_implicit_parens": {
117
+ "active": 0,
118
+ "flagValue": -1
119
+ },
120
+ "no_interpolation_in_single_quotes": {
121
+ "active": 0,
122
+ "flagValue": -1
123
+ },
124
+ "no_plusplus": {
125
+ "active": 0,
126
+ "flagValue": -1
127
+ },
128
+ "no_stand_alone_at": {
129
+ "active": 1,
130
+ "flagValue": -1
131
+ },
132
+ "no_tabs": {
133
+ "active": 1,
134
+ "flagValue": -1
135
+ },
136
+ "no_throwing_strings": {
137
+ "active": 1,
138
+ "flagValue": -1
139
+ },
140
+ "no_trailing_semicolons": {
141
+ "active": 1,
142
+ "flagValue": -1
143
+ },
144
+ "no_trailing_whitespace": {
145
+ "active": 1,
146
+ "flagValue": -1
147
+ },
148
+ "no_unnecessary_double_quotes": {
149
+ "active": 0,
150
+ "flagValue": -1
151
+ },
152
+ "no_unnecessary_fat_arrows": {
153
+ "active": 1,
154
+ "flagValue": -1
155
+ },
156
+ "non_empty_constructor_needs_parens": {
157
+ "active": 0,
158
+ "flagValue": -1
159
+ },
160
+ "prefer_english_operator": {
161
+ "active": 0,
162
+ "flagValue": -1
163
+ },
164
+ "space_operators": {
165
+ "active": 0,
166
+ "flagValue": -1
167
+ }
168
+ },
169
+ "coffeeMinifyOutput": 1,
170
+ "coffeeOutputStyle": 0,
171
+ "coffeeSyntaxCheckerStyle": 1,
172
+ "externalServerAddress": "http:\/\/localhost:8888",
173
+ "externalServerPreviewPathAddition": "",
174
+ "genericWebpageFileExtensionsString": "html, htm, shtml, shtm, xhtml, php, jsp, asp, aspx, erb, ctp",
175
+ "hamlAutoOutputPathEnabled": 1,
176
+ "hamlAutoOutputPathFilenamePattern": "*.html",
177
+ "hamlAutoOutputPathRelativePath": "",
178
+ "hamlAutoOutputPathReplace1": "",
179
+ "hamlAutoOutputPathReplace2": "",
180
+ "hamlAutoOutputPathStyle": 0,
181
+ "hamlEscapeHTMLCharacters": 0,
182
+ "hamlNoEscapeInAttributes": 0,
183
+ "hamlOutputFormat": 2,
184
+ "hamlOutputStyle": 0,
185
+ "hamlUseCDATA": 0,
186
+ "hamlUseDoubleQuotes": 0,
187
+ "hamlUseUnixNewlines": 0,
188
+ "jadeAutoOutputPathEnabled": 1,
189
+ "jadeAutoOutputPathFilenamePattern": "*.html",
190
+ "jadeAutoOutputPathRelativePath": "",
191
+ "jadeAutoOutputPathReplace1": "",
192
+ "jadeAutoOutputPathReplace2": "",
193
+ "jadeAutoOutputPathStyle": 0,
194
+ "jadeCompileDebug": 1,
195
+ "jadeOutputStyle": 0,
196
+ "javascriptAutoOutputPathEnabled": 1,
197
+ "javascriptAutoOutputPathFilenamePattern": "*-min.js",
198
+ "javascriptAutoOutputPathRelativePath": "\/min",
199
+ "javascriptAutoOutputPathReplace1": "",
200
+ "javascriptAutoOutputPathReplace2": "",
201
+ "javascriptAutoOutputPathStyle": 2,
202
+ "javascriptCreateSourceMap": 1,
203
+ "javascriptOutputStyle": 1,
204
+ "javascriptSyntaxCheckerStyle": 1,
205
+ "jsCheckerReservedNamesString": "",
206
+ "jsHintFlags2": {
207
+ "asi": {
208
+ "active": 0,
209
+ "flagValue": -1
210
+ },
211
+ "bitwise": {
212
+ "active": 1,
213
+ "flagValue": -1
214
+ },
215
+ "boss": {
216
+ "active": 0,
217
+ "flagValue": -1
218
+ },
219
+ "browser": {
220
+ "active": 1,
221
+ "flagValue": -1
222
+ },
223
+ "camelcase": {
224
+ "active": 0,
225
+ "flagValue": -1
226
+ },
227
+ "couch": {
228
+ "active": 0,
229
+ "flagValue": -1
230
+ },
231
+ "curly": {
232
+ "active": 1,
233
+ "flagValue": -1
234
+ },
235
+ "debug": {
236
+ "active": 0,
237
+ "flagValue": -1
238
+ },
239
+ "devel": {
240
+ "active": 0,
241
+ "flagValue": -1
242
+ },
243
+ "dojo": {
244
+ "active": 0,
245
+ "flagValue": -1
246
+ },
247
+ "eqeqeq": {
248
+ "active": 1,
249
+ "flagValue": -1
250
+ },
251
+ "eqnull": {
252
+ "active": 0,
253
+ "flagValue": -1
254
+ },
255
+ "es3": {
256
+ "active": 0,
257
+ "flagValue": -1
258
+ },
259
+ "esnext": {
260
+ "active": 0,
261
+ "flagValue": -1
262
+ },
263
+ "evil": {
264
+ "active": 0,
265
+ "flagValue": -1
266
+ },
267
+ "expr": {
268
+ "active": 0,
269
+ "flagValue": -1
270
+ },
271
+ "forin": {
272
+ "active": 0,
273
+ "flagValue": -1
274
+ },
275
+ "freeze": {
276
+ "active": 1,
277
+ "flagValue": -1
278
+ },
279
+ "funcscope": {
280
+ "active": 0,
281
+ "flagValue": -1
282
+ },
283
+ "globalstrict": {
284
+ "active": 0,
285
+ "flagValue": -1
286
+ },
287
+ "immed": {
288
+ "active": 0,
289
+ "flagValue": -1
290
+ },
291
+ "indent": {
292
+ "active": 0,
293
+ "flagValue": 4
294
+ },
295
+ "iterator": {
296
+ "active": 0,
297
+ "flagValue": -1
298
+ },
299
+ "jquery": {
300
+ "active": 1,
301
+ "flagValue": -1
302
+ },
303
+ "lastsemic": {
304
+ "active": 0,
305
+ "flagValue": -1
306
+ },
307
+ "latedef": {
308
+ "active": 1,
309
+ "flagValue": -1
310
+ },
311
+ "laxbreak": {
312
+ "active": 0,
313
+ "flagValue": -1
314
+ },
315
+ "laxcomma": {
316
+ "active": 0,
317
+ "flagValue": -1
318
+ },
319
+ "loopfunc": {
320
+ "active": 0,
321
+ "flagValue": -1
322
+ },
323
+ "maxcomplexity": {
324
+ "active": 0,
325
+ "flagValue": 10
326
+ },
327
+ "maxdepth": {
328
+ "active": 0,
329
+ "flagValue": 3
330
+ },
331
+ "maxlen": {
332
+ "active": 0,
333
+ "flagValue": 150
334
+ },
335
+ "maxparams": {
336
+ "active": 0,
337
+ "flagValue": 3
338
+ },
339
+ "maxstatements": {
340
+ "active": 0,
341
+ "flagValue": 4
342
+ },
343
+ "mootools": {
344
+ "active": 0,
345
+ "flagValue": -1
346
+ },
347
+ "moz": {
348
+ "active": 0,
349
+ "flagValue": -1
350
+ },
351
+ "multistr": {
352
+ "active": 0,
353
+ "flagValue": -1
354
+ },
355
+ "newcap": {
356
+ "active": 1,
357
+ "flagValue": -1
358
+ },
359
+ "noarg": {
360
+ "active": 1,
361
+ "flagValue": -1
362
+ },
363
+ "node": {
364
+ "active": 0,
365
+ "flagValue": -1
366
+ },
367
+ "noempty": {
368
+ "active": 0,
369
+ "flagValue": -1
370
+ },
371
+ "nonbsp": {
372
+ "active": 0,
373
+ "flagValue": -1
374
+ },
375
+ "nonew": {
376
+ "active": 1,
377
+ "flagValue": -1
378
+ },
379
+ "nonstandard": {
380
+ "active": 0,
381
+ "flagValue": -1
382
+ },
383
+ "notypeof": {
384
+ "active": 1,
385
+ "flagValue": -1
386
+ },
387
+ "noyield": {
388
+ "active": 0,
389
+ "flagValue": -1
390
+ },
391
+ "onecase": {
392
+ "active": 0,
393
+ "flagValue": -1
394
+ },
395
+ "phantom": {
396
+ "active": 0,
397
+ "flagValue": -1
398
+ },
399
+ "plusplus": {
400
+ "active": 0,
401
+ "flagValue": -1
402
+ },
403
+ "proto": {
404
+ "active": 0,
405
+ "flagValue": -1
406
+ },
407
+ "prototypejs": {
408
+ "active": 0,
409
+ "flagValue": -1
410
+ },
411
+ "regexp": {
412
+ "active": 1,
413
+ "flagValue": -1
414
+ },
415
+ "rhino": {
416
+ "active": 0,
417
+ "flagValue": -1
418
+ },
419
+ "scripturl": {
420
+ "active": 0,
421
+ "flagValue": -1
422
+ },
423
+ "shadow": {
424
+ "active": 0,
425
+ "flagValue": -1
426
+ },
427
+ "shelljs": {
428
+ "active": 0,
429
+ "flagValue": -1
430
+ },
431
+ "singleGroups": {
432
+ "active": 0,
433
+ "flagValue": -1
434
+ },
435
+ "strict": {
436
+ "active": 0,
437
+ "flagValue": -1
438
+ },
439
+ "sub": {
440
+ "active": 0,
441
+ "flagValue": -1
442
+ },
443
+ "supernew": {
444
+ "active": 0,
445
+ "flagValue": -1
446
+ },
447
+ "typed": {
448
+ "active": 0,
449
+ "flagValue": -1
450
+ },
451
+ "undef": {
452
+ "active": 1,
453
+ "flagValue": -1
454
+ },
455
+ "unused": {
456
+ "active": 1,
457
+ "flagValue": -1
458
+ },
459
+ "withstmt": {
460
+ "active": 0,
461
+ "flagValue": -1
462
+ },
463
+ "worker": {
464
+ "active": 0,
465
+ "flagValue": -1
466
+ },
467
+ "wsh": {
468
+ "active": 0,
469
+ "flagValue": -1
470
+ },
471
+ "yui": {
472
+ "active": 0,
473
+ "flagValue": -1
474
+ }
475
+ },
476
+ "jsLintFlags2": {
477
+ "ass": {
478
+ "active": 0,
479
+ "flagValue": -1
480
+ },
481
+ "bitwise": {
482
+ "active": 0,
483
+ "flagValue": -1
484
+ },
485
+ "browser": {
486
+ "active": 1,
487
+ "flagValue": -1
488
+ },
489
+ "closure": {
490
+ "active": 0,
491
+ "flagValue": -1
492
+ },
493
+ "continue": {
494
+ "active": 0,
495
+ "flagValue": -1
496
+ },
497
+ "debug": {
498
+ "active": 0,
499
+ "flagValue": -1
500
+ },
501
+ "devel": {
502
+ "active": 0,
503
+ "flagValue": -1
504
+ },
505
+ "eqeq": {
506
+ "active": 0,
507
+ "flagValue": -1
508
+ },
509
+ "evil": {
510
+ "active": 0,
511
+ "flagValue": -1
512
+ },
513
+ "forin": {
514
+ "active": 0,
515
+ "flagValue": -1
516
+ },
517
+ "indent": {
518
+ "active": 0,
519
+ "flagValue": 4
520
+ },
521
+ "maxlen": {
522
+ "active": 0,
523
+ "flagValue": 150
524
+ },
525
+ "newcap": {
526
+ "active": 0,
527
+ "flagValue": -1
528
+ },
529
+ "node": {
530
+ "active": 0,
531
+ "flagValue": -1
532
+ },
533
+ "nomen": {
534
+ "active": 0,
535
+ "flagValue": -1
536
+ },
537
+ "plusplus": {
538
+ "active": 0,
539
+ "flagValue": -1
540
+ },
541
+ "properties": {
542
+ "active": 0,
543
+ "flagValue": -1
544
+ },
545
+ "regexp": {
546
+ "active": 0,
547
+ "flagValue": -1
548
+ },
549
+ "rhino": {
550
+ "active": 0,
551
+ "flagValue": -1
552
+ },
553
+ "sloppy": {
554
+ "active": 0,
555
+ "flagValue": -1
556
+ },
557
+ "stupid": {
558
+ "active": 0,
559
+ "flagValue": -1
560
+ },
561
+ "sub": {
562
+ "active": 0,
563
+ "flagValue": -1
564
+ },
565
+ "todo": {
566
+ "active": 0,
567
+ "flagValue": -1
568
+ },
569
+ "unparam": {
570
+ "active": 0,
571
+ "flagValue": -1
572
+ },
573
+ "vars": {
574
+ "active": 0,
575
+ "flagValue": -1
576
+ },
577
+ "white": {
578
+ "active": 0,
579
+ "flagValue": -1
580
+ }
581
+ },
582
+ "kitAutoOutputPathEnabled": 1,
583
+ "kitAutoOutputPathFilenamePattern": "*.html",
584
+ "kitAutoOutputPathRelativePath": "",
585
+ "kitAutoOutputPathReplace1": "",
586
+ "kitAutoOutputPathReplace2": "",
587
+ "kitAutoOutputPathStyle": 0,
588
+ "lessAllowInsecureImports": 0,
589
+ "lessAutoOutputPathEnabled": 1,
590
+ "lessAutoOutputPathFilenamePattern": "*.css",
591
+ "lessAutoOutputPathRelativePath": "..\/css",
592
+ "lessAutoOutputPathReplace1": "less",
593
+ "lessAutoOutputPathReplace2": "css",
594
+ "lessAutoOutputPathStyle": 2,
595
+ "lessCreateSourceMap": 0,
596
+ "lessDisableJavascript": 0,
597
+ "lessIeCompatibility": 1,
598
+ "lessOutputStyle": 0,
599
+ "lessRelativeURLS": 0,
600
+ "lessStrictImports": 0,
601
+ "lessStrictMath": 0,
602
+ "lessStrictUnits": 0,
603
+ "markdownAutoOutputPathEnabled": 1,
604
+ "markdownAutoOutputPathFilenamePattern": "*.html",
605
+ "markdownAutoOutputPathRelativePath": "",
606
+ "markdownAutoOutputPathReplace1": "",
607
+ "markdownAutoOutputPathReplace2": "",
608
+ "markdownAutoOutputPathStyle": 0,
609
+ "markdownEnableFootnotes": 0,
610
+ "markdownEnableSmartyPants": 1,
611
+ "markdownExpandTabs": 1,
612
+ "reloadFileURLs": 0,
613
+ "sassAutoOutputPathEnabled": 1,
614
+ "sassAutoOutputPathFilenamePattern": "*.css",
615
+ "sassAutoOutputPathRelativePath": "..\/css",
616
+ "sassAutoOutputPathReplace1": "sass",
617
+ "sassAutoOutputPathReplace2": "css",
618
+ "sassAutoOutputPathStyle": 2,
619
+ "sassCreateSourceMap": 0,
620
+ "sassDebugStyle": 0,
621
+ "sassDecimalPrecision": 10,
622
+ "sassOutputStyle": 0,
623
+ "sassUseLibsass": 0,
624
+ "shouldRunAutoprefixer": 0,
625
+ "shouldRunBless": 0,
626
+ "skippedItemsString": ".svn, .git, .hg, log, _logs, _cache, cache, logs, node_modules",
627
+ "slimAutoOutputPathEnabled": 1,
628
+ "slimAutoOutputPathFilenamePattern": "*.html",
629
+ "slimAutoOutputPathRelativePath": "",
630
+ "slimAutoOutputPathReplace1": "",
631
+ "slimAutoOutputPathReplace2": "",
632
+ "slimAutoOutputPathStyle": 0,
633
+ "slimCompileOnly": 0,
634
+ "slimLogicless": 0,
635
+ "slimOutputStyle": 1,
636
+ "slimRailsCompatible": 0,
637
+ "stylusAutoOutputPathEnabled": 1,
638
+ "stylusAutoOutputPathFilenamePattern": "*.css",
639
+ "stylusAutoOutputPathRelativePath": "..\/css",
640
+ "stylusAutoOutputPathReplace1": "stylus",
641
+ "stylusAutoOutputPathReplace2": "css",
642
+ "stylusAutoOutputPathStyle": 2,
643
+ "stylusCreateSourceMap": 0,
644
+ "stylusDebugStyle": 0,
645
+ "stylusImportCSS": 0,
646
+ "stylusOutputStyle": 0,
647
+ "stylusResolveRelativeURLS": 0,
648
+ "typescriptAutoOutputPathEnabled": 1,
649
+ "typescriptAutoOutputPathFilenamePattern": "*.js",
650
+ "typescriptAutoOutputPathRelativePath": "\/js",
651
+ "typescriptAutoOutputPathReplace1": "",
652
+ "typescriptAutoOutputPathReplace2": "",
653
+ "typescriptAutoOutputPathStyle": 2,
654
+ "typescriptCreateDeclarationFile": 0,
655
+ "typescriptCreateSourceMap": 0,
656
+ "typescriptMinifyOutput": 0,
657
+ "typescriptModuleType": 0,
658
+ "typescriptNoImplicitAny": 0,
659
+ "typescriptNoResolve": 0,
660
+ "typescriptRemoveComments": 0,
661
+ "typescriptTargetECMAVersion": 0,
662
+ "uglifyDefinesString": "",
663
+ "uglifyFlags2": {
664
+ "ascii-only": {
665
+ "active": 0,
666
+ "flagValue": -1
667
+ },
668
+ "booleans": {
669
+ "active": 1,
670
+ "flagValue": -1
671
+ },
672
+ "bracketize": {
673
+ "active": 0,
674
+ "flagValue": -1
675
+ },
676
+ "cascade": {
677
+ "active": 1,
678
+ "flagValue": -1
679
+ },
680
+ "comments": {
681
+ "active": 1,
682
+ "flagValue": -1
683
+ },
684
+ "comparisons": {
685
+ "active": 1,
686
+ "flagValue": -1
687
+ },
688
+ "compress": {
689
+ "active": 1,
690
+ "flagValue": -1
691
+ },
692
+ "conditionals": {
693
+ "active": 1,
694
+ "flagValue": -1
695
+ },
696
+ "dead_code": {
697
+ "active": 0,
698
+ "flagValue": -1
699
+ },
700
+ "drop_debugger": {
701
+ "active": 1,
702
+ "flagValue": -1
703
+ },
704
+ "eval": {
705
+ "active": 0,
706
+ "flagValue": -1
707
+ },
708
+ "evaluate": {
709
+ "active": 1,
710
+ "flagValue": -1
711
+ },
712
+ "hoist_funs": {
713
+ "active": 1,
714
+ "flagValue": -1
715
+ },
716
+ "hoist_vars": {
717
+ "active": 0,
718
+ "flagValue": -1
719
+ },
720
+ "if_return": {
721
+ "active": 1,
722
+ "flagValue": -1
723
+ },
724
+ "indent-level": {
725
+ "active": 0,
726
+ "flagValue": 4
727
+ },
728
+ "indent-start": {
729
+ "active": 0,
730
+ "flagValue": 0
731
+ },
732
+ "inline-script": {
733
+ "active": 0,
734
+ "flagValue": -1
735
+ },
736
+ "join_vars": {
737
+ "active": 1,
738
+ "flagValue": -1
739
+ },
740
+ "loops": {
741
+ "active": 1,
742
+ "flagValue": -1
743
+ },
744
+ "mangle": {
745
+ "active": 1,
746
+ "flagValue": -1
747
+ },
748
+ "max-line-len": {
749
+ "active": 1,
750
+ "flagValue": 32000
751
+ },
752
+ "properties": {
753
+ "active": 1,
754
+ "flagValue": -1
755
+ },
756
+ "quote-keys": {
757
+ "active": 0,
758
+ "flagValue": -1
759
+ },
760
+ "screw-ie8": {
761
+ "active": 0,
762
+ "flagValue": -1
763
+ },
764
+ "semicolons": {
765
+ "active": 1,
766
+ "flagValue": -1
767
+ },
768
+ "sequences": {
769
+ "active": 1,
770
+ "flagValue": -1
771
+ },
772
+ "sort": {
773
+ "active": 0,
774
+ "flagValue": -1
775
+ },
776
+ "space-colon": {
777
+ "active": 1,
778
+ "flagValue": -1
779
+ },
780
+ "toplevel": {
781
+ "active": 0,
782
+ "flagValue": -1
783
+ },
784
+ "unsafe": {
785
+ "active": 0,
786
+ "flagValue": -1
787
+ },
788
+ "unused": {
789
+ "active": 0,
790
+ "flagValue": -1
791
+ },
792
+ "warnings": {
793
+ "active": 0,
794
+ "flagValue": -1
795
+ },
796
+ "width": {
797
+ "active": 1,
798
+ "flagValue": 80
799
+ }
800
+ },
801
+ "uglifyReservedNamesString": "$",
802
+ "websiteRelativeRoot": ""
803
+ },
804
+ "settingsFileVersion": "2"
805
+ }
skin/frontend/base/default/css/gene/doddle/doddle.css ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'doddle-icon';
3
+ src: url('fonts/doddle-icon.eot?-s7vh28');
4
+ src: url('fonts/doddle-icon.eot?#iefix-s7vh28') format('embedded-opentype'), url('fonts/doddle-icon.woff?-s7vh28') format('woff'), url('fonts/doddle-icon.ttf?-s7vh28') format('truetype'), url('fonts/doddle-icon.svg?-s7vh28#icomoon') format('svg');
5
+ font-weight: normal;
6
+ font-style: normal;
7
+ }
8
+ .form-list .control.doddle-control {
9
+ vertical-align: middle;
10
+ line-height: 21px;
11
+ position: relative;
12
+ }
13
+ .form-list .control.doddle-control label .ship-to {
14
+ float: left;
15
+ }
16
+ .form-list .control.doddle-control label img.doddle-logo {
17
+ margin-left: 6px;
18
+ float: left;
19
+ height: 18px;
20
+ }
21
+ .form-list .control.doddle-control:hover .doddle-billing-more-information {
22
+ visibility: visible;
23
+ opacity: 1;
24
+ transition-delay: 0s;
25
+ }
26
+ .form-list .control.doddle-control .doddle-billing-more-information {
27
+ visibility: hidden;
28
+ opacity: 0;
29
+ transition: visibility 0s ease-in-out 0.15s, opacity 0.15s ease-in-out;
30
+ position: absolute;
31
+ left: 0;
32
+ right: -200px;
33
+ bottom: 100%;
34
+ width: 400px;
35
+ background: white;
36
+ border: 1px solid #dbdbdb;
37
+ padding: 16px;
38
+ }
39
+ .form-list .control.doddle-control .doddle-billing-more-information:hover {
40
+ visibility: visible;
41
+ opacity: 1;
42
+ transition-delay: 0s;
43
+ }
44
+ #doddle-box {
45
+ width: 100%;
46
+ padding: 20px 24px;
47
+ background: #f4f4f4;
48
+ margin-bottom: 16px;
49
+ }
50
+ #doddle-box .doddle-header {
51
+ float: left;
52
+ width: 100%;
53
+ }
54
+ #doddle-box .doddle-header .doddle-logo {
55
+ height: 40px;
56
+ margin-bottom: 12px;
57
+ margin-top: 4px;
58
+ display: inline;
59
+ }
60
+ #doddle-box .doddle-header .whats-this {
61
+ line-height: 44px;
62
+ display: inline;
63
+ vertical-align: text-bottom;
64
+ margin-left: 4px;
65
+ }
66
+ #doddle-box .doddle-header #doddle-more-information {
67
+ float: left;
68
+ width: 100%;
69
+ background: white;
70
+ padding: 14px 18px;
71
+ margin: 0 0 14px 0;
72
+ }
73
+ #doddle-box ul.messages {
74
+ margin: 8px 0 12px 0;
75
+ }
76
+ #doddle-box .hidden-validation {
77
+ display: none;
78
+ }
79
+ #doddle-box table {
80
+ text-align: left;
81
+ margin: 8px 0;
82
+ }
83
+ #doddle-box table td.input {
84
+ text-align: center;
85
+ }
86
+ #doddle-box table td.input .validation-advice {
87
+ display: none;
88
+ }
89
+ #doddle-box table td,
90
+ #doddle-box table th {
91
+ padding: 8px 10px;
92
+ border-bottom: 2px solid #dbdbdb;
93
+ }
94
+ #doddle-box table td label {
95
+ display: block;
96
+ width: 100%;
97
+ }
98
+ #doddle-box table th {
99
+ font-family: "Raleway", "Helvetica Neue", Verdana, Arial, sans-serif;
100
+ font-size: 14px;
101
+ text-transform: uppercase;
102
+ }
103
+ #doddle-box table address {
104
+ font-size: 90%;
105
+ }
106
+ #doddle-box table .more-information-td {
107
+ position: relative;
108
+ }
109
+ #doddle-box table .more-information-td .doddle-info {
110
+ font-family: 'doddle-icon';
111
+ speak: none;
112
+ font-style: normal;
113
+ font-weight: normal;
114
+ font-variant: normal;
115
+ text-transform: none;
116
+ line-height: 1;
117
+ font-size: 18px;
118
+ text-decoration: none;
119
+ /* Better Font Rendering =========== */
120
+ -webkit-font-smoothing: antialiased;
121
+ -moz-osx-font-smoothing: grayscale;
122
+ }
123
+ #doddle-box table .more-information-td .doddle-info:hover + .more-information {
124
+ visibility: visible;
125
+ opacity: 1;
126
+ transition-delay: 0s;
127
+ }
128
+ #doddle-box table .more-information-td .more-information {
129
+ visibility: hidden;
130
+ opacity: 0;
131
+ transition: visibility 0s ease-in-out 0.15s, opacity 0.15s ease-in-out;
132
+ background: white;
133
+ border: 1px solid #dbdbdb;
134
+ padding: 16px;
135
+ position: absolute;
136
+ left: -220px;
137
+ bottom: 90%;
138
+ right: -220px;
139
+ }
140
+ #doddle-box table .more-information-td .more-information .opening-times {
141
+ float: left;
142
+ width: 47.5%;
143
+ margin-right: 2.5%;
144
+ }
145
+ #doddle-box table .more-information-td .more-information .doddle-map {
146
+ float: left;
147
+ width: 47.5%;
148
+ margin-left: 2.5%;
149
+ }
150
+ #doddle-box table .more-information-td .more-information table {
151
+ width: 100%;
152
+ }
153
+ #doddle-box table .more-information-td .more-information table.opening-times-table td,
154
+ #doddle-box table .more-information-td .more-information table.opening-times-table th {
155
+ padding: 4px 5px;
156
+ border-bottom: 2px solid #f4f4f4;
157
+ }
158
+ #doddle-box table .more-information-td .more-information table.opening-times-table td {
159
+ text-align: right;
160
+ }
161
+ #doddle-box table .more-information-td .more-information:hover {
162
+ visibility: visible;
163
+ opacity: 1;
164
+ transition-delay: 0s;
165
+ }
166
+ #doddle-box .doddle-search {
167
+ margin: 8px 0;
168
+ }
169
+ #doddle-box .doddle-search .doddle-search-button {
170
+ margin-right: 8px;
171
+ }
172
+ #doddle-box .doddle-search .doddle-search-form {
173
+ display: inline-block;
174
+ *zoom: 1;
175
+ *display: inline;
176
+ }
177
+ #doddle-box .doddle-search .doddle-search-input {
178
+ margin-left: 8px;
179
+ text-transform: uppercase;
180
+ max-width: 250px;
181
+ height: 33px;
182
+ }
183
+ #doddle-box .button.loading {
184
+ background: white url('../../../images/opc-ajax-loader.gif') center center no-repeat;
185
+ background-size: 24px 24px;
186
+ }
187
+ #doddle-box .button.loading span {
188
+ opacity: 0;
189
+ }
190
+ .doddle-sidebar-logo {
191
+ margin: 10px 0 8px 0;
192
+ width: 120px;
193
+ }
194
+ @media only screen and (max-width: 980px) {
195
+ #doddle-box table .more-information-td .more-information {
196
+ left: -440px;
197
+ right: 0;
198
+ }
199
+ }
200
+ @media only screen and (max-width: 680px) {
201
+ #doddle-box .doddle-search .doddle-search-button {
202
+ display: block;
203
+ width: 100%;
204
+ margin: 6px 0;
205
+ }
206
+ #doddle-box .doddle-search .doddle-or {
207
+ display: block;
208
+ width: 100%;
209
+ text-align: center;
210
+ }
211
+ #doddle-box .doddle-search .doddle-search-form {
212
+ display: block;
213
+ width: 100%;
214
+ }
215
+ #doddle-box .doddle-search .doddle-search-form .doddle-search-input {
216
+ margin: 6px 0 2px 0;
217
+ width: 100%;
218
+ display: block;
219
+ max-width: none;
220
+ }
221
+ }
skin/frontend/base/default/css/gene/doddle/doddle.less ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'doddle-icon';
3
+ src:url('fonts/doddle-icon.eot?-s7vh28');
4
+ src:url('fonts/doddle-icon.eot?#iefix-s7vh28') format('embedded-opentype'),
5
+ url('fonts/doddle-icon.woff?-s7vh28') format('woff'),
6
+ url('fonts/doddle-icon.ttf?-s7vh28') format('truetype'),
7
+ url('fonts/doddle-icon.svg?-s7vh28#icomoon') format('svg');
8
+ font-weight: normal;
9
+ font-style: normal;
10
+ }
11
+
12
+ .form-list {
13
+ .control {
14
+ &.doddle-control {
15
+ vertical-align: middle;
16
+ line-height: 21px;
17
+ position: relative;
18
+
19
+ label {
20
+
21
+ .ship-to {
22
+ float: left;
23
+ }
24
+
25
+ img.doddle-logo {
26
+ margin-left: 6px;
27
+ float: left;
28
+ height: 18px;
29
+ }
30
+ }
31
+
32
+ &:hover {
33
+ .doddle-billing-more-information {
34
+ visibility: visible;
35
+ opacity: 1;
36
+ transition-delay: 0s;
37
+ }
38
+ }
39
+
40
+ .doddle-billing-more-information {
41
+ visibility: hidden;
42
+ opacity: 0;
43
+ transition: visibility 0s ease-in-out 0.15s, opacity 0.15s ease-in-out;
44
+
45
+ position: absolute;
46
+ left: 0;
47
+ right: -200px;
48
+ bottom: 100%;
49
+ width: 400px;
50
+ background: white;
51
+ border: 1px solid #dbdbdb;
52
+ padding: 16px;
53
+
54
+ &:hover {
55
+ visibility: visible;
56
+ opacity: 1;
57
+ transition-delay: 0s;
58
+ }
59
+ }
60
+
61
+ }
62
+ }
63
+ }
64
+
65
+ #doddle-box {
66
+ width: 100%;
67
+ padding: 20px 24px;
68
+ background: #f4f4f4;
69
+ margin-bottom: 16px;
70
+
71
+ .doddle-header {
72
+ float: left;
73
+ width: 100%;
74
+
75
+ .doddle-logo {
76
+ height: 40px;
77
+ margin-bottom: 12px;
78
+ margin-top: 4px;
79
+ display: inline;
80
+ }
81
+
82
+ .whats-this {
83
+ line-height: 44px;
84
+ display: inline;
85
+ vertical-align: text-bottom;
86
+ margin-left: 4px;
87
+ }
88
+
89
+ #doddle-more-information {
90
+ float: left;
91
+ width: 100%;
92
+ background: white;
93
+ padding: 14px 18px;
94
+ margin: 0 0 14px 0;
95
+ }
96
+ }
97
+
98
+
99
+ ul.messages {
100
+ margin: 8px 0 12px 0;
101
+ }
102
+
103
+ .hidden-validation {
104
+ display: none;
105
+ }
106
+
107
+ table {
108
+
109
+ text-align: left;
110
+ margin: 8px 0;
111
+
112
+ td.input {
113
+ text-align: center;
114
+
115
+ .validation-advice {
116
+ display: none;
117
+ }
118
+ }
119
+
120
+ td, th {
121
+ padding: 8px 10px;
122
+ border-bottom: 2px solid #dbdbdb;
123
+ }
124
+
125
+ td {
126
+ label {
127
+ display: block;
128
+ width: 100%;
129
+ }
130
+ }
131
+
132
+ th {
133
+ font-family: "Raleway", "Helvetica Neue", Verdana, Arial, sans-serif;
134
+ font-size: 14px;
135
+ text-transform: uppercase;
136
+ }
137
+
138
+ address {
139
+ font-size: 90%;
140
+ }
141
+
142
+ .more-information-td {
143
+ position: relative;
144
+
145
+ .doddle-info {
146
+ font-family: 'doddle-icon';
147
+ speak: none;
148
+ font-style: normal;
149
+ font-weight: normal;
150
+ font-variant: normal;
151
+ text-transform: none;
152
+ line-height: 1;
153
+ font-size: 18px;
154
+ text-decoration: none;
155
+
156
+ /* Better Font Rendering =========== */
157
+ -webkit-font-smoothing: antialiased;
158
+ -moz-osx-font-smoothing: grayscale;
159
+
160
+ &:hover + .more-information {
161
+ visibility: visible;
162
+ opacity: 1;
163
+ transition-delay: 0s;
164
+ }
165
+ }
166
+
167
+ .more-information {
168
+ visibility: hidden;
169
+ opacity: 0;
170
+ transition: visibility 0s ease-in-out 0.15s, opacity 0.15s ease-in-out;
171
+
172
+ background: white;
173
+ border: 1px solid #dbdbdb;
174
+ padding: 16px;
175
+ position: absolute;
176
+ left: -220px;
177
+ bottom: 90%;
178
+ right: -220px;
179
+
180
+ .opening-times {
181
+ float: left;
182
+ width: 47.5%;
183
+ margin-right: 2.5%;
184
+ }
185
+
186
+ .doddle-map {
187
+ float: left;
188
+ width: 47.5%;
189
+ margin-left: 2.5%;
190
+ }
191
+
192
+ table {
193
+ width: 100%;
194
+
195
+ &.opening-times-table {
196
+ td, th {
197
+ padding: 4px 5px;
198
+ border-bottom: 2px solid #f4f4f4;
199
+ }
200
+
201
+ td {
202
+ text-align: right;
203
+ }
204
+ }
205
+ }
206
+
207
+ &:hover {
208
+ visibility: visible;
209
+ opacity: 1;
210
+ transition-delay: 0s;
211
+ }
212
+ }
213
+ }
214
+ }
215
+
216
+ .doddle-search {
217
+ margin: 8px 0;
218
+
219
+ .doddle-search-button {
220
+ margin-right: 8px;
221
+ }
222
+
223
+ .doddle-search-form {
224
+ display: inline-block;
225
+ *zoom: 1;
226
+ *display: inline;
227
+ }
228
+
229
+ .doddle-search-input {
230
+ margin-left: 8px;
231
+ text-transform: uppercase;
232
+ max-width: 250px;
233
+ height: 33px;
234
+ }
235
+ }
236
+
237
+ .button.loading {
238
+ background: white url('../../../images/gene/doddle/opc-ajax-loader.gif') center center no-repeat;
239
+ background-size: 24px 24px;
240
+
241
+ span {
242
+ opacity: 0;
243
+ }
244
+ }
245
+ }
246
+
247
+ .doddle-sidebar-logo {
248
+ margin: 10px 0 8px 0;
249
+ width: 120px;
250
+ }
251
+
252
+
253
+ @media only screen and (max-width: 980px) {
254
+
255
+ #doddle-box {
256
+
257
+ table {
258
+
259
+ .more-information-td {
260
+
261
+ .more-information {
262
+ left: -440px;
263
+ right: 0;
264
+ }
265
+
266
+ }
267
+
268
+ }
269
+
270
+ }
271
+
272
+ }
273
+
274
+ @media only screen and (max-width: 680px) {
275
+
276
+ #doddle-box {
277
+ .doddle-search {
278
+
279
+ .doddle-search-button {
280
+ display: block;
281
+ width: 100%;
282
+ margin: 6px 0;
283
+ }
284
+
285
+ .doddle-or {
286
+ display: block;
287
+ width: 100%;
288
+ text-align: center;
289
+ }
290
+
291
+ .doddle-search-form {
292
+ display: block;
293
+ width: 100%;
294
+
295
+ .doddle-search-input {
296
+ margin: 6px 0 2px 0;
297
+ width: 100%;
298
+ display: block;
299
+ max-width: none;
300
+ }
301
+ }
302
+
303
+ }
304
+ }
305
+ }
skin/frontend/base/default/css/gene/doddle/fonts/doddle-icon.eot ADDED
Binary file
skin/frontend/base/default/css/gene/doddle/fonts/doddle-icon.svg ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <metadata>Generated by IcoMoon</metadata>
5
+ <defs>
6
+ <font id="icomoon" horiz-adv-x="1024">
7
+ <font-face units-per-em="1024" ascent="960" descent="-64" />
8
+ <missing-glyph horiz-adv-x="1024" />
9
+ <glyph unicode="&#x20;" d="" horiz-adv-x="512" />
10
+ <glyph unicode="&#x69;" d="M515.021 957.368c-282.767 0-512-229.233-512-512 0-282.757 229.233-511.99 512-511.99s512 229.233 512 511.99c0 282.767-229.233 512-512 512zM514.376 726.18c11.469 12.452 25.446 18.78 41.523 18.78 13.036 0 24.504-4.69 33.116-13.568 8.663-8.827 13.056-20.122 13.056-33.536 0-16.814-5.878-31.764-17.459-44.431-11.899-13.066-25.815-19.712-41.318-19.712-12.8 0-24.136 4.803-32.778 13.885-8.561 8.899-12.933 20.531-12.933 34.519 0 17.132 5.652 31.959 16.794 44.063zM599.665 243.804c-42.445-39.916-72.745-64.799-92.59-76.104-21.176-12.124-38.625-18.043-53.32-18.043-14.602 0-26.511 4.864-35.42 14.438-8.632 9.257-13.005 21.76-13.005 37.12 0 39.26 22.241 130.437 68.024 278.743 0.778 2.509 1.382 4.895 1.792 7.117-1.71-0.737-3.686-1.782-5.98-3.205-3.748-2.365-15.165-11.325-48.579-43.971-5.1-4.987-13.076-5.448-18.719-1.075l-19.763 15.288c-3.154 2.427-5.14 6.072-5.489 10.045-0.348 3.963 0.983 7.895 3.656 10.844 30.792 33.905 59.535 58.522 85.443 73.165 27.116 15.278 50.043 22.712 70.062 22.712 12.943 0 23.398-3.215 31.099-9.585 8.376-6.984 12.8-16.681 12.8-28.058 0-7.465-3.543-26.911-29.696-114.616-37.356-125.317-46.285-170.803-46.889-186.542 7.066 4.178 23.788 16.056 59.484 49.603 5.56 5.263 14.285 5.161 19.743-0.164l17.521-17.080c2.796-2.724 4.362-6.461 4.321-10.363s-1.659-7.598-4.495-10.271z" />
11
+ </font></defs></svg>
skin/frontend/base/default/css/gene/doddle/fonts/doddle-icon.ttf ADDED
Binary file
skin/frontend/base/default/css/gene/doddle/fonts/doddle-icon.woff ADDED
Binary file
skin/frontend/base/default/images/gene/doddle/logo.png ADDED
Binary file
skin/frontend/base/default/images/gene/doddle/opc-ajax-loader.gif ADDED
Binary file
skin/frontend/rwd/default/css/gene/doddle/config.codekit ADDED
@@ -0,0 +1,805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "CodeKitInfo": "This is a CodeKit 2.x project configuration file. It is designed to sync project settings across multiple machines. MODIFYING THE CONTENTS OF THIS FILE IS A POOR LIFE DECISION. If you do so, you will likely cause CodeKit to crash. This file is not useful unless accompanied by the project that created it in CodeKit 2. This file is not backwards-compatible with CodeKit 1.x. For more information, see: http:\/\/incident57.com\/codekit",
3
+ "creatorBuild": "18270",
4
+ "files": {
5
+ "\/doddle.less": {
6
+ "allowInsecureImports": 0,
7
+ "createSourceMap": 0,
8
+ "disableJavascript": 0,
9
+ "fileType": 1,
10
+ "ieCompatibility": 1,
11
+ "ignore": 0,
12
+ "ignoreWasSetByUser": 0,
13
+ "inputAbbreviatedPath": "\/doddle.less",
14
+ "outputAbbreviatedPath": "\/doddle.css",
15
+ "outputPathIsOutsideProject": 0,
16
+ "outputPathIsSetByUser": 1,
17
+ "outputStyle": 0,
18
+ "relativeURLS": 0,
19
+ "shouldRunAutoprefixer": 0,
20
+ "shouldRunBless": 0,
21
+ "strictImports": 0,
22
+ "strictMath": 0,
23
+ "strictUnits": 0
24
+ }
25
+ },
26
+ "hooks": [
27
+ ],
28
+ "lastSavedByUser": "David Macaulay",
29
+ "manualImportLinks": {
30
+ },
31
+ "projectAttributes": {
32
+ "bowerAbbreviatedPath": "",
33
+ "displayValue": "doddle",
34
+ "displayValueWasSetByUser": 0,
35
+ "iconImageName": "brackets_gray"
36
+ },
37
+ "projectSettings": {
38
+ "alwaysUseExternalServer": 0,
39
+ "animateCSSInjections": 1,
40
+ "autoApplyPSLanguageSettingsStyle": 0,
41
+ "autoprefixerBrowserString": "> 1%, last 2 versions, Firefox ESR, Opera 12.1",
42
+ "autoSyncProjectSettingsFile": 1,
43
+ "browserRefreshDelay": 0,
44
+ "coffeeAutoOutputPathEnabled": 1,
45
+ "coffeeAutoOutputPathFilenamePattern": "*.js",
46
+ "coffeeAutoOutputPathRelativePath": "",
47
+ "coffeeAutoOutputPathReplace1": "",
48
+ "coffeeAutoOutputPathReplace2": "",
49
+ "coffeeAutoOutputPathStyle": 0,
50
+ "coffeeCreateSourceMap": 0,
51
+ "coffeeLintFlags2": {
52
+ "arrow_spacing": {
53
+ "active": 0,
54
+ "flagValue": -1
55
+ },
56
+ "camel_case_classes": {
57
+ "active": 1,
58
+ "flagValue": -1
59
+ },
60
+ "colon_assignment_spacing": {
61
+ "active": 0,
62
+ "flagValue": 1
63
+ },
64
+ "cyclomatic_complexity": {
65
+ "active": 0,
66
+ "flagValue": 10
67
+ },
68
+ "duplicate_key": {
69
+ "active": 1,
70
+ "flagValue": -1
71
+ },
72
+ "empty_constructor_needs_parens": {
73
+ "active": 0,
74
+ "flagValue": -1
75
+ },
76
+ "indentation": {
77
+ "active": 1,
78
+ "flagValue": 2
79
+ },
80
+ "line_endings": {
81
+ "active": 0,
82
+ "flagValue": 0
83
+ },
84
+ "max_line_length": {
85
+ "active": 0,
86
+ "flagValue": 150
87
+ },
88
+ "missing_fat_arrows": {
89
+ "active": 0,
90
+ "flagValue": -1
91
+ },
92
+ "newlines_after_classes": {
93
+ "active": 0,
94
+ "flagValue": 3
95
+ },
96
+ "no_backticks": {
97
+ "active": 1,
98
+ "flagValue": -1
99
+ },
100
+ "no_debugger": {
101
+ "active": 1,
102
+ "flagValue": -1
103
+ },
104
+ "no_empty_functions": {
105
+ "active": 0,
106
+ "flagValue": -1
107
+ },
108
+ "no_empty_param_list": {
109
+ "active": 0,
110
+ "flagValue": -1
111
+ },
112
+ "no_implicit_braces": {
113
+ "active": 1,
114
+ "flagValue": -1
115
+ },
116
+ "no_implicit_parens": {
117
+ "active": 0,
118
+ "flagValue": -1
119
+ },
120
+ "no_interpolation_in_single_quotes": {
121
+ "active": 0,
122
+ "flagValue": -1
123
+ },
124
+ "no_plusplus": {
125
+ "active": 0,
126
+ "flagValue": -1
127
+ },
128
+ "no_stand_alone_at": {
129
+ "active": 1,
130
+ "flagValue": -1
131
+ },
132
+ "no_tabs": {
133
+ "active": 1,
134
+ "flagValue": -1
135
+ },
136
+ "no_throwing_strings": {
137
+ "active": 1,
138
+ "flagValue": -1
139
+ },
140
+ "no_trailing_semicolons": {
141
+ "active": 1,
142
+ "flagValue": -1
143
+ },
144
+ "no_trailing_whitespace": {
145
+ "active": 1,
146
+ "flagValue": -1
147
+ },
148
+ "no_unnecessary_double_quotes": {
149
+ "active": 0,
150
+ "flagValue": -1
151
+ },
152
+ "no_unnecessary_fat_arrows": {
153
+ "active": 1,
154
+ "flagValue": -1
155
+ },
156
+ "non_empty_constructor_needs_parens": {
157
+ "active": 0,
158
+ "flagValue": -1
159
+ },
160
+ "prefer_english_operator": {
161
+ "active": 0,
162
+ "flagValue": -1
163
+ },
164
+ "space_operators": {
165
+ "active": 0,
166
+ "flagValue": -1
167
+ }
168
+ },
169
+ "coffeeMinifyOutput": 1,
170
+ "coffeeOutputStyle": 0,
171
+ "coffeeSyntaxCheckerStyle": 1,
172
+ "externalServerAddress": "http:\/\/localhost:8888",
173
+ "externalServerPreviewPathAddition": "",
174
+ "genericWebpageFileExtensionsString": "html, htm, shtml, shtm, xhtml, php, jsp, asp, aspx, erb, ctp",
175
+ "hamlAutoOutputPathEnabled": 1,
176
+ "hamlAutoOutputPathFilenamePattern": "*.html",
177
+ "hamlAutoOutputPathRelativePath": "",
178
+ "hamlAutoOutputPathReplace1": "",
179
+ "hamlAutoOutputPathReplace2": "",
180
+ "hamlAutoOutputPathStyle": 0,
181
+ "hamlEscapeHTMLCharacters": 0,
182
+ "hamlNoEscapeInAttributes": 0,
183
+ "hamlOutputFormat": 2,
184
+ "hamlOutputStyle": 0,
185
+ "hamlUseCDATA": 0,
186
+ "hamlUseDoubleQuotes": 0,
187
+ "hamlUseUnixNewlines": 0,
188
+ "jadeAutoOutputPathEnabled": 1,
189
+ "jadeAutoOutputPathFilenamePattern": "*.html",
190
+ "jadeAutoOutputPathRelativePath": "",
191
+ "jadeAutoOutputPathReplace1": "",
192
+ "jadeAutoOutputPathReplace2": "",
193
+ "jadeAutoOutputPathStyle": 0,
194
+ "jadeCompileDebug": 1,
195
+ "jadeOutputStyle": 0,
196
+ "javascriptAutoOutputPathEnabled": 1,
197
+ "javascriptAutoOutputPathFilenamePattern": "*-min.js",
198
+ "javascriptAutoOutputPathRelativePath": "\/min",
199
+ "javascriptAutoOutputPathReplace1": "",
200
+ "javascriptAutoOutputPathReplace2": "",
201
+ "javascriptAutoOutputPathStyle": 2,
202
+ "javascriptCreateSourceMap": 1,
203
+ "javascriptOutputStyle": 1,
204
+ "javascriptSyntaxCheckerStyle": 1,
205
+ "jsCheckerReservedNamesString": "",
206
+ "jsHintFlags2": {
207
+ "asi": {
208
+ "active": 0,
209
+ "flagValue": -1
210
+ },
211
+ "bitwise": {
212
+ "active": 1,
213
+ "flagValue": -1
214
+ },
215
+ "boss": {
216
+ "active": 0,
217
+ "flagValue": -1
218
+ },
219
+ "browser": {
220
+ "active": 1,
221
+ "flagValue": -1
222
+ },
223
+ "camelcase": {
224
+ "active": 0,
225
+ "flagValue": -1
226
+ },
227
+ "couch": {
228
+ "active": 0,
229
+ "flagValue": -1
230
+ },
231
+ "curly": {
232
+ "active": 1,
233
+ "flagValue": -1
234
+ },
235
+ "debug": {
236
+ "active": 0,
237
+ "flagValue": -1
238
+ },
239
+ "devel": {
240
+ "active": 0,
241
+ "flagValue": -1
242
+ },
243
+ "dojo": {
244
+ "active": 0,
245
+ "flagValue": -1
246
+ },
247
+ "eqeqeq": {
248
+ "active": 1,
249
+ "flagValue": -1
250
+ },
251
+ "eqnull": {
252
+ "active": 0,
253
+ "flagValue": -1
254
+ },
255
+ "es3": {
256
+ "active": 0,
257
+ "flagValue": -1
258
+ },
259
+ "esnext": {
260
+ "active": 0,
261
+ "flagValue": -1
262
+ },
263
+ "evil": {
264
+ "active": 0,
265
+ "flagValue": -1
266
+ },
267
+ "expr": {
268
+ "active": 0,
269
+ "flagValue": -1
270
+ },
271
+ "forin": {
272
+ "active": 0,
273
+ "flagValue": -1
274
+ },
275
+ "freeze": {
276
+ "active": 1,
277
+ "flagValue": -1
278
+ },
279
+ "funcscope": {
280
+ "active": 0,
281
+ "flagValue": -1
282
+ },
283
+ "globalstrict": {
284
+ "active": 0,
285
+ "flagValue": -1
286
+ },
287
+ "immed": {
288
+ "active": 0,
289
+ "flagValue": -1
290
+ },
291
+ "indent": {
292
+ "active": 0,
293
+ "flagValue": 4
294
+ },
295
+ "iterator": {
296
+ "active": 0,
297
+ "flagValue": -1
298
+ },
299
+ "jquery": {
300
+ "active": 1,
301
+ "flagValue": -1
302
+ },
303
+ "lastsemic": {
304
+ "active": 0,
305
+ "flagValue": -1
306
+ },
307
+ "latedef": {
308
+ "active": 1,
309
+ "flagValue": -1
310
+ },
311
+ "laxbreak": {
312
+ "active": 0,
313
+ "flagValue": -1
314
+ },
315
+ "laxcomma": {
316
+ "active": 0,
317
+ "flagValue": -1
318
+ },
319
+ "loopfunc": {
320
+ "active": 0,
321
+ "flagValue": -1
322
+ },
323
+ "maxcomplexity": {
324
+ "active": 0,
325
+ "flagValue": 10
326
+ },
327
+ "maxdepth": {
328
+ "active": 0,
329
+ "flagValue": 3
330
+ },
331
+ "maxlen": {
332
+ "active": 0,
333
+ "flagValue": 150
334
+ },
335
+ "maxparams": {
336
+ "active": 0,
337
+ "flagValue": 3
338
+ },
339
+ "maxstatements": {
340
+ "active": 0,
341
+ "flagValue": 4
342
+ },
343
+ "mootools": {
344
+ "active": 0,
345
+ "flagValue": -1
346
+ },
347
+ "moz": {
348
+ "active": 0,
349
+ "flagValue": -1
350
+ },
351
+ "multistr": {
352
+ "active": 0,
353
+ "flagValue": -1
354
+ },
355
+ "newcap": {
356
+ "active": 1,
357
+ "flagValue": -1
358
+ },
359
+ "noarg": {
360
+ "active": 1,
361
+ "flagValue": -1
362
+ },
363
+ "node": {
364
+ "active": 0,
365
+ "flagValue": -1
366
+ },
367
+ "noempty": {
368
+ "active": 0,
369
+ "flagValue": -1
370
+ },
371
+ "nonbsp": {
372
+ "active": 0,
373
+ "flagValue": -1
374
+ },
375
+ "nonew": {
376
+ "active": 1,
377
+ "flagValue": -1
378
+ },
379
+ "nonstandard": {
380
+ "active": 0,
381
+ "flagValue": -1
382
+ },
383
+ "notypeof": {
384
+ "active": 1,
385
+ "flagValue": -1
386
+ },
387
+ "noyield": {
388
+ "active": 0,
389
+ "flagValue": -1
390
+ },
391
+ "onecase": {
392
+ "active": 0,
393
+ "flagValue": -1
394
+ },
395
+ "phantom": {
396
+ "active": 0,
397
+ "flagValue": -1
398
+ },
399
+ "plusplus": {
400
+ "active": 0,
401
+ "flagValue": -1
402
+ },
403
+ "proto": {
404
+ "active": 0,
405
+ "flagValue": -1
406
+ },
407
+ "prototypejs": {
408
+ "active": 0,
409
+ "flagValue": -1
410
+ },
411
+ "regexp": {
412
+ "active": 1,
413
+ "flagValue": -1
414
+ },
415
+ "rhino": {
416
+ "active": 0,
417
+ "flagValue": -1
418
+ },
419
+ "scripturl": {
420
+ "active": 0,
421
+ "flagValue": -1
422
+ },
423
+ "shadow": {
424
+ "active": 0,
425
+ "flagValue": -1
426
+ },
427
+ "shelljs": {
428
+ "active": 0,
429
+ "flagValue": -1
430
+ },
431
+ "singleGroups": {
432
+ "active": 0,
433
+ "flagValue": -1
434
+ },
435
+ "strict": {
436
+ "active": 0,
437
+ "flagValue": -1
438
+ },
439
+ "sub": {
440
+ "active": 0,
441
+ "flagValue": -1
442
+ },
443
+ "supernew": {
444
+ "active": 0,
445
+ "flagValue": -1
446
+ },
447
+ "typed": {
448
+ "active": 0,
449
+ "flagValue": -1
450
+ },
451
+ "undef": {
452
+ "active": 1,
453
+ "flagValue": -1
454
+ },
455
+ "unused": {
456
+ "active": 1,
457
+ "flagValue": -1
458
+ },
459
+ "withstmt": {
460
+ "active": 0,
461
+ "flagValue": -1
462
+ },
463
+ "worker": {
464
+ "active": 0,
465
+ "flagValue": -1
466
+ },
467
+ "wsh": {
468
+ "active": 0,
469
+ "flagValue": -1
470
+ },
471
+ "yui": {
472
+ "active": 0,
473
+ "flagValue": -1
474
+ }
475
+ },
476
+ "jsLintFlags2": {
477
+ "ass": {
478
+ "active": 0,
479
+ "flagValue": -1
480
+ },
481
+ "bitwise": {
482
+ "active": 0,
483
+ "flagValue": -1
484
+ },
485
+ "browser": {
486
+ "active": 1,
487
+ "flagValue": -1
488
+ },
489
+ "closure": {
490
+ "active": 0,
491
+ "flagValue": -1
492
+ },
493
+ "continue": {
494
+ "active": 0,
495
+ "flagValue": -1
496
+ },
497
+ "debug": {
498
+ "active": 0,
499
+ "flagValue": -1
500
+ },
501
+ "devel": {
502
+ "active": 0,
503
+ "flagValue": -1
504
+ },
505
+ "eqeq": {
506
+ "active": 0,
507
+ "flagValue": -1
508
+ },
509
+ "evil": {
510
+ "active": 0,
511
+ "flagValue": -1
512
+ },
513
+ "forin": {
514
+ "active": 0,
515
+ "flagValue": -1
516
+ },
517
+ "indent": {
518
+ "active": 0,
519
+ "flagValue": 4
520
+ },
521
+ "maxlen": {
522
+ "active": 0,
523
+ "flagValue": 150
524
+ },
525
+ "newcap": {
526
+ "active": 0,
527
+ "flagValue": -1
528
+ },
529
+ "node": {
530
+ "active": 0,
531
+ "flagValue": -1
532
+ },
533
+ "nomen": {
534
+ "active": 0,
535
+ "flagValue": -1
536
+ },
537
+ "plusplus": {
538
+ "active": 0,
539
+ "flagValue": -1
540
+ },
541
+ "properties": {
542
+ "active": 0,
543
+ "flagValue": -1
544
+ },
545
+ "regexp": {
546
+ "active": 0,
547
+ "flagValue": -1
548
+ },
549
+ "rhino": {
550
+ "active": 0,
551
+ "flagValue": -1
552
+ },
553
+ "sloppy": {
554
+ "active": 0,
555
+ "flagValue": -1
556
+ },
557
+ "stupid": {
558
+ "active": 0,
559
+ "flagValue": -1
560
+ },
561
+ "sub": {
562
+ "active": 0,
563
+ "flagValue": -1
564
+ },
565
+ "todo": {
566
+ "active": 0,
567
+ "flagValue": -1
568
+ },
569
+ "unparam": {
570
+ "active": 0,
571
+ "flagValue": -1
572
+ },
573
+ "vars": {
574
+ "active": 0,
575
+ "flagValue": -1
576
+ },
577
+ "white": {
578
+ "active": 0,
579
+ "flagValue": -1
580
+ }
581
+ },
582
+ "kitAutoOutputPathEnabled": 1,
583
+ "kitAutoOutputPathFilenamePattern": "*.html",
584
+ "kitAutoOutputPathRelativePath": "",
585
+ "kitAutoOutputPathReplace1": "",
586
+ "kitAutoOutputPathReplace2": "",
587
+ "kitAutoOutputPathStyle": 0,
588
+ "lessAllowInsecureImports": 0,
589
+ "lessAutoOutputPathEnabled": 1,
590
+ "lessAutoOutputPathFilenamePattern": "*.css",
591
+ "lessAutoOutputPathRelativePath": "..\/css",
592
+ "lessAutoOutputPathReplace1": "less",
593
+ "lessAutoOutputPathReplace2": "css",
594
+ "lessAutoOutputPathStyle": 2,
595
+ "lessCreateSourceMap": 0,
596
+ "lessDisableJavascript": 0,
597
+ "lessIeCompatibility": 1,
598
+ "lessOutputStyle": 0,
599
+ "lessRelativeURLS": 0,
600
+ "lessStrictImports": 0,
601
+ "lessStrictMath": 0,
602
+ "lessStrictUnits": 0,
603
+ "markdownAutoOutputPathEnabled": 1,
604
+ "markdownAutoOutputPathFilenamePattern": "*.html",
605
+ "markdownAutoOutputPathRelativePath": "",
606
+ "markdownAutoOutputPathReplace1": "",
607
+ "markdownAutoOutputPathReplace2": "",
608
+ "markdownAutoOutputPathStyle": 0,
609
+ "markdownEnableFootnotes": 0,
610
+ "markdownEnableSmartyPants": 1,
611
+ "markdownExpandTabs": 1,
612
+ "reloadFileURLs": 0,
613
+ "sassAutoOutputPathEnabled": 1,
614
+ "sassAutoOutputPathFilenamePattern": "*.css",
615
+ "sassAutoOutputPathRelativePath": "..\/css",
616
+ "sassAutoOutputPathReplace1": "sass",
617
+ "sassAutoOutputPathReplace2": "css",
618
+ "sassAutoOutputPathStyle": 2,
619
+ "sassCreateSourceMap": 0,
620
+ "sassDebugStyle": 0,
621
+ "sassDecimalPrecision": 10,
622
+ "sassOutputStyle": 0,
623
+ "sassUseLibsass": 0,
624
+ "shouldRunAutoprefixer": 0,
625
+ "shouldRunBless": 0,
626
+ "skippedItemsString": ".svn, .git, .hg, log, _logs, _cache, cache, logs, node_modules",
627
+ "slimAutoOutputPathEnabled": 1,
628
+ "slimAutoOutputPathFilenamePattern": "*.html",
629
+ "slimAutoOutputPathRelativePath": "",
630
+ "slimAutoOutputPathReplace1": "",
631
+ "slimAutoOutputPathReplace2": "",
632
+ "slimAutoOutputPathStyle": 0,
633
+ "slimCompileOnly": 0,
634
+ "slimLogicless": 0,
635
+ "slimOutputStyle": 1,
636
+ "slimRailsCompatible": 0,
637
+ "stylusAutoOutputPathEnabled": 1,
638
+ "stylusAutoOutputPathFilenamePattern": "*.css",
639
+ "stylusAutoOutputPathRelativePath": "..\/css",
640
+ "stylusAutoOutputPathReplace1": "stylus",
641
+ "stylusAutoOutputPathReplace2": "css",
642
+ "stylusAutoOutputPathStyle": 2,
643
+ "stylusCreateSourceMap": 0,
644
+ "stylusDebugStyle": 0,
645
+ "stylusImportCSS": 0,
646
+ "stylusOutputStyle": 0,
647
+ "stylusResolveRelativeURLS": 0,
648
+ "typescriptAutoOutputPathEnabled": 1,
649
+ "typescriptAutoOutputPathFilenamePattern": "*.js",
650
+ "typescriptAutoOutputPathRelativePath": "\/js",
651
+ "typescriptAutoOutputPathReplace1": "",
652
+ "typescriptAutoOutputPathReplace2": "",
653
+ "typescriptAutoOutputPathStyle": 2,
654
+ "typescriptCreateDeclarationFile": 0,
655
+ "typescriptCreateSourceMap": 0,
656
+ "typescriptMinifyOutput": 0,
657
+ "typescriptModuleType": 0,
658
+ "typescriptNoImplicitAny": 0,
659
+ "typescriptNoResolve": 0,
660
+ "typescriptRemoveComments": 0,
661
+ "typescriptTargetECMAVersion": 0,
662
+ "uglifyDefinesString": "",
663
+ "uglifyFlags2": {
664
+ "ascii-only": {
665
+ "active": 0,
666
+ "flagValue": -1
667
+ },
668
+ "booleans": {
669
+ "active": 1,
670
+ "flagValue": -1
671
+ },
672
+ "bracketize": {
673
+ "active": 0,
674
+ "flagValue": -1
675
+ },
676
+ "cascade": {
677
+ "active": 1,
678
+ "flagValue": -1
679
+ },
680
+ "comments": {
681
+ "active": 1,
682
+ "flagValue": -1
683
+ },
684
+ "comparisons": {
685
+ "active": 1,
686
+ "flagValue": -1
687
+ },
688
+ "compress": {
689
+ "active": 1,
690
+ "flagValue": -1
691
+ },
692
+ "conditionals": {
693
+ "active": 1,
694
+ "flagValue": -1
695
+ },
696
+ "dead_code": {
697
+ "active": 0,
698
+ "flagValue": -1
699
+ },
700
+ "drop_debugger": {
701
+ "active": 1,
702
+ "flagValue": -1
703
+ },
704
+ "eval": {
705
+ "active": 0,
706
+ "flagValue": -1
707
+ },
708
+ "evaluate": {
709
+ "active": 1,
710
+ "flagValue": -1
711
+ },
712
+ "hoist_funs": {
713
+ "active": 1,
714
+ "flagValue": -1
715
+ },
716
+ "hoist_vars": {
717
+ "active": 0,
718
+ "flagValue": -1
719
+ },
720
+ "if_return": {
721
+ "active": 1,
722
+ "flagValue": -1
723
+ },
724
+ "indent-level": {
725
+ "active": 0,
726
+ "flagValue": 4
727
+ },
728
+ "indent-start": {
729
+ "active": 0,
730
+ "flagValue": 0
731
+ },
732
+ "inline-script": {
733
+ "active": 0,
734
+ "flagValue": -1
735
+ },
736
+ "join_vars": {
737
+ "active": 1,
738
+ "flagValue": -1
739
+ },
740
+ "loops": {
741
+ "active": 1,
742
+ "flagValue": -1
743
+ },
744
+ "mangle": {
745
+ "active": 1,
746
+ "flagValue": -1
747
+ },
748
+ "max-line-len": {
749
+ "active": 1,
750
+ "flagValue": 32000
751
+ },
752
+ "properties": {
753
+ "active": 1,
754
+ "flagValue": -1
755
+ },
756
+ "quote-keys": {
757
+ "active": 0,
758
+ "flagValue": -1
759
+ },
760
+ "screw-ie8": {
761
+ "active": 0,
762
+ "flagValue": -1
763
+ },
764
+ "semicolons": {
765
+ "active": 1,
766
+ "flagValue": -1
767
+ },
768
+ "sequences": {
769
+ "active": 1,
770
+ "flagValue": -1
771
+ },
772
+ "sort": {
773
+ "active": 0,
774
+ "flagValue": -1
775
+ },
776
+ "space-colon": {
777
+ "active": 1,
778
+ "flagValue": -1
779
+ },
780
+ "toplevel": {
781
+ "active": 0,
782
+ "flagValue": -1
783
+ },
784
+ "unsafe": {
785
+ "active": 0,
786
+ "flagValue": -1
787
+ },
788
+ "unused": {
789
+ "active": 0,
790
+ "flagValue": -1
791
+ },
792
+ "warnings": {
793
+ "active": 0,
794
+ "flagValue": -1
795
+ },
796
+ "width": {
797
+ "active": 1,
798
+ "flagValue": 80
799
+ }
800
+ },
801
+ "uglifyReservedNamesString": "$",
802
+ "websiteRelativeRoot": ""
803
+ },
804
+ "settingsFileVersion": "2"
805
+ }
skin/frontend/rwd/default/css/gene/doddle/doddle.css ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'doddle-icon';
3
+ src: url('fonts/doddle-icon.eot?-s7vh28');
4
+ src: url('fonts/doddle-icon.eot?#iefix-s7vh28') format('embedded-opentype'), url('fonts/doddle-icon.woff?-s7vh28') format('woff'), url('fonts/doddle-icon.ttf?-s7vh28') format('truetype'), url('fonts/doddle-icon.svg?-s7vh28#icomoon') format('svg');
5
+ font-weight: normal;
6
+ font-style: normal;
7
+ }
8
+ .form-list .control.doddle-control {
9
+ vertical-align: middle;
10
+ line-height: 21px;
11
+ position: relative;
12
+ }
13
+ .form-list .control.doddle-control label .ship-to {
14
+ float: left;
15
+ }
16
+ .form-list .control.doddle-control label img.doddle-logo {
17
+ margin-left: 6px;
18
+ float: left;
19
+ height: 18px;
20
+ }
21
+ .form-list .control.doddle-control:hover .doddle-billing-more-information {
22
+ visibility: visible;
23
+ opacity: 1;
24
+ transition-delay: 0s;
25
+ }
26
+ .form-list .control.doddle-control .doddle-billing-more-information {
27
+ visibility: hidden;
28
+ opacity: 0;
29
+ transition: visibility 0s ease-in-out 0.15s, opacity 0.15s ease-in-out;
30
+ position: absolute;
31
+ left: 0;
32
+ right: -200px;
33
+ bottom: 100%;
34
+ width: 400px;
35
+ background: white;
36
+ border: 1px solid #dbdbdb;
37
+ padding: 16px;
38
+ }
39
+ .form-list .control.doddle-control .doddle-billing-more-information:hover {
40
+ visibility: visible;
41
+ opacity: 1;
42
+ transition-delay: 0s;
43
+ }
44
+ #doddle-box {
45
+ width: 100%;
46
+ padding: 20px 24px;
47
+ background: #f4f4f4;
48
+ margin-bottom: 16px;
49
+ }
50
+ #doddle-box .doddle-header {
51
+ float: left;
52
+ width: 100%;
53
+ }
54
+ #doddle-box .doddle-header .doddle-logo {
55
+ height: 40px;
56
+ margin-bottom: 12px;
57
+ margin-top: 4px;
58
+ display: inline;
59
+ }
60
+ #doddle-box .doddle-header .whats-this {
61
+ line-height: 44px;
62
+ display: inline;
63
+ vertical-align: text-bottom;
64
+ margin-left: 4px;
65
+ }
66
+ #doddle-box .doddle-header #doddle-more-information {
67
+ float: left;
68
+ width: 100%;
69
+ background: white;
70
+ padding: 14px 18px;
71
+ margin: 0 0 14px 0;
72
+ }
73
+ #doddle-box ul.messages {
74
+ margin: 8px 0 12px 0;
75
+ }
76
+ #doddle-box .hidden-validation {
77
+ display: none;
78
+ }
79
+ #doddle-box table {
80
+ text-align: left;
81
+ margin: 8px 0;
82
+ }
83
+ #doddle-box table td.input {
84
+ text-align: center;
85
+ }
86
+ #doddle-box table td.input .validation-advice {
87
+ display: none;
88
+ }
89
+ #doddle-box table td,
90
+ #doddle-box table th {
91
+ padding: 8px 10px;
92
+ border-bottom: 2px solid #dbdbdb;
93
+ }
94
+ #doddle-box table td label {
95
+ display: block;
96
+ width: 100%;
97
+ }
98
+ #doddle-box table th {
99
+ font-family: "Raleway", "Helvetica Neue", Verdana, Arial, sans-serif;
100
+ font-size: 14px;
101
+ text-transform: uppercase;
102
+ }
103
+ #doddle-box table address {
104
+ font-size: 90%;
105
+ }
106
+ #doddle-box table .more-information-td {
107
+ position: relative;
108
+ }
109
+ #doddle-box table .more-information-td .doddle-info {
110
+ font-family: 'doddle-icon';
111
+ speak: none;
112
+ font-style: normal;
113
+ font-weight: normal;
114
+ font-variant: normal;
115
+ text-transform: none;
116
+ line-height: 1;
117
+ font-size: 18px;
118
+ text-decoration: none;
119
+ /* Better Font Rendering =========== */
120
+ -webkit-font-smoothing: antialiased;
121
+ -moz-osx-font-smoothing: grayscale;
122
+ }
123
+ #doddle-box table .more-information-td .doddle-info:hover + .more-information {
124
+ visibility: visible;
125
+ opacity: 1;
126
+ transition-delay: 0s;
127
+ }
128
+ #doddle-box table .more-information-td .more-information {
129
+ visibility: hidden;
130
+ opacity: 0;
131
+ transition: visibility 0s ease-in-out 0.15s, opacity 0.15s ease-in-out;
132
+ background: white;
133
+ border: 1px solid #dbdbdb;
134
+ padding: 16px;
135
+ position: absolute;
136
+ left: -220px;
137
+ bottom: 90%;
138
+ right: -220px;
139
+ }
140
+ #doddle-box table .more-information-td .more-information .opening-times {
141
+ float: left;
142
+ width: 47.5%;
143
+ margin-right: 2.5%;
144
+ }
145
+ #doddle-box table .more-information-td .more-information .doddle-map {
146
+ float: left;
147
+ width: 47.5%;
148
+ margin-left: 2.5%;
149
+ }
150
+ #doddle-box table .more-information-td .more-information table {
151
+ width: 100%;
152
+ }
153
+ #doddle-box table .more-information-td .more-information table.opening-times-table td,
154
+ #doddle-box table .more-information-td .more-information table.opening-times-table th {
155
+ padding: 4px 5px;
156
+ border-bottom: 2px solid #f4f4f4;
157
+ }
158
+ #doddle-box table .more-information-td .more-information table.opening-times-table td {
159
+ text-align: right;
160
+ }
161
+ #doddle-box table .more-information-td .more-information:hover {
162
+ visibility: visible;
163
+ opacity: 1;
164
+ transition-delay: 0s;
165
+ }
166
+ #doddle-box .doddle-search {
167
+ margin: 8px 0;
168
+ }
169
+ #doddle-box .doddle-search .doddle-search-button {
170
+ margin-right: 8px;
171
+ }
172
+ #doddle-box .doddle-search .doddle-search-form {
173
+ display: inline-block;
174
+ *zoom: 1;
175
+ *display: inline;
176
+ }
177
+ #doddle-box .doddle-search .doddle-search-input {
178
+ margin-left: 8px;
179
+ text-transform: uppercase;
180
+ max-width: 250px;
181
+ height: 33px;
182
+ }
183
+ #doddle-box .button.loading {
184
+ background: white url('../../../images/opc-ajax-loader.gif') center center no-repeat;
185
+ background-size: 24px 24px;
186
+ }
187
+ #doddle-box .button.loading span {
188
+ opacity: 0;
189
+ }
190
+ .doddle-sidebar-logo {
191
+ margin: 10px 0 8px 0;
192
+ width: 120px;
193
+ }
194
+ @media only screen and (max-width: 980px) {
195
+ #doddle-box table .more-information-td .more-information {
196
+ left: -440px;
197
+ right: 0;
198
+ }
199
+ }
200
+ @media only screen and (max-width: 680px) {
201
+ #doddle-box .doddle-search .doddle-search-button {
202
+ display: block;
203
+ width: 100%;
204
+ margin: 6px 0;
205
+ }
206
+ #doddle-box .doddle-search .doddle-or {
207
+ display: block;
208
+ width: 100%;
209
+ text-align: center;
210
+ }
211
+ #doddle-box .doddle-search .doddle-search-form {
212
+ display: block;
213
+ width: 100%;
214
+ }
215
+ #doddle-box .doddle-search .doddle-search-form .doddle-search-input {
216
+ margin: 6px 0 2px 0;
217
+ width: 100%;
218
+ display: block;
219
+ max-width: none;
220
+ }
221
+ }
skin/frontend/rwd/default/css/gene/doddle/doddle.less ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'doddle-icon';
3
+ src:url('fonts/doddle-icon.eot?-s7vh28');
4
+ src:url('fonts/doddle-icon.eot?#iefix-s7vh28') format('embedded-opentype'),
5
+ url('fonts/doddle-icon.woff?-s7vh28') format('woff'),
6
+ url('fonts/doddle-icon.ttf?-s7vh28') format('truetype'),
7
+ url('fonts/doddle-icon.svg?-s7vh28#icomoon') format('svg');
8
+ font-weight: normal;
9
+ font-style: normal;
10
+ }
11
+
12
+ .form-list {
13
+ .control {
14
+ &.doddle-control {
15
+ vertical-align: middle;
16
+ line-height: 21px;
17
+ position: relative;
18
+
19
+ label {
20
+
21
+ .ship-to {
22
+ float: left;
23
+ }
24
+
25
+ img.doddle-logo {
26
+ margin-left: 6px;
27
+ float: left;
28
+ height: 18px;
29
+ }
30
+ }
31
+
32
+ &:hover {
33
+ .doddle-billing-more-information {
34
+ visibility: visible;
35
+ opacity: 1;
36
+ transition-delay: 0s;
37
+ }
38
+ }
39
+
40
+ .doddle-billing-more-information {
41
+ visibility: hidden;
42
+ opacity: 0;
43
+ transition: visibility 0s ease-in-out 0.15s, opacity 0.15s ease-in-out;
44
+
45
+ position: absolute;
46
+ left: 0;
47
+ right: -200px;
48
+ bottom: 100%;
49
+ width: 400px;
50
+ background: white;
51
+ border: 1px solid #dbdbdb;
52
+ padding: 16px;
53
+
54
+ &:hover {
55
+ visibility: visible;
56
+ opacity: 1;
57
+ transition-delay: 0s;
58
+ }
59
+ }
60
+
61
+ }
62
+ }
63
+ }
64
+
65
+ #doddle-box {
66
+ width: 100%;
67
+ padding: 20px 24px;
68
+ background: #f4f4f4;
69
+ margin-bottom: 16px;
70
+
71
+ .doddle-header {
72
+ float: left;
73
+ width: 100%;
74
+
75
+ .doddle-logo {
76
+ height: 40px;
77
+ margin-bottom: 12px;
78
+ margin-top: 4px;
79
+ display: inline;
80
+ }
81
+
82
+ .whats-this {
83
+ line-height: 44px;
84
+ display: inline;
85
+ vertical-align: text-bottom;
86
+ margin-left: 4px;
87
+ }
88
+
89
+ #doddle-more-information {
90
+ float: left;
91
+ width: 100%;
92
+ background: white;
93
+ padding: 14px 18px;
94
+ margin: 0 0 14px 0;
95
+ }
96
+ }
97
+
98
+
99
+ ul.messages {
100
+ margin: 8px 0 12px 0;
101
+ }
102
+
103
+ .hidden-validation {
104
+ display: none;
105
+ }
106
+
107
+ table {
108
+
109
+ text-align: left;
110
+ margin: 8px 0;
111
+
112
+ td.input {
113
+ text-align: center;
114
+
115
+ .validation-advice {
116
+ display: none;
117
+ }
118
+ }
119
+
120
+ td, th {
121
+ padding: 8px 10px;
122
+ border-bottom: 2px solid #dbdbdb;
123
+ }
124
+
125
+ td {
126
+ label {
127
+ display: block;
128
+ width: 100%;
129
+ }
130
+ }
131
+
132
+ th {
133
+ font-family: "Raleway", "Helvetica Neue", Verdana, Arial, sans-serif;
134
+ font-size: 14px;
135
+ text-transform: uppercase;
136
+ }
137
+
138
+ address {
139
+ font-size: 90%;
140
+ }
141
+
142
+ .more-information-td {
143
+ position: relative;
144
+
145
+ .doddle-info {
146
+ font-family: 'doddle-icon';
147
+ speak: none;
148
+ font-style: normal;
149
+ font-weight: normal;
150
+ font-variant: normal;
151
+ text-transform: none;
152
+ line-height: 1;
153
+ font-size: 18px;
154
+ text-decoration: none;
155
+
156
+ /* Better Font Rendering =========== */
157
+ -webkit-font-smoothing: antialiased;
158
+ -moz-osx-font-smoothing: grayscale;
159
+
160
+ &:hover + .more-information {
161
+ visibility: visible;
162
+ opacity: 1;
163
+ transition-delay: 0s;
164
+ }
165
+ }
166
+
167
+ .more-information {
168
+ visibility: hidden;
169
+ opacity: 0;
170
+ transition: visibility 0s ease-in-out 0.15s, opacity 0.15s ease-in-out;
171
+
172
+ background: white;
173
+ border: 1px solid #dbdbdb;
174
+ padding: 16px;
175
+ position: absolute;
176
+ left: -220px;
177
+ bottom: 90%;
178
+ right: -220px;
179
+
180
+ .opening-times {
181
+ float: left;
182
+ width: 47.5%;
183
+ margin-right: 2.5%;
184
+ }
185
+
186
+ .doddle-map {
187
+ float: left;
188
+ width: 47.5%;
189
+ margin-left: 2.5%;
190
+ }
191
+
192
+ table {
193
+ width: 100%;
194
+
195
+ &.opening-times-table {
196
+ td, th {
197
+ padding: 4px 5px;
198
+ border-bottom: 2px solid #f4f4f4;
199
+ }
200
+
201
+ td {
202
+ text-align: right;
203
+ }
204
+ }
205
+ }
206
+
207
+ &:hover {
208
+ visibility: visible;
209
+ opacity: 1;
210
+ transition-delay: 0s;
211
+ }
212
+ }
213
+ }
214
+ }
215
+
216
+ .doddle-search {
217
+ margin: 8px 0;
218
+
219
+ .doddle-search-button {
220
+ margin-right: 8px;
221
+ }
222
+
223
+ .doddle-search-form {
224
+ display: inline-block;
225
+ *zoom: 1;
226
+ *display: inline;
227
+ }
228
+
229
+ .doddle-search-input {
230
+ margin-left: 8px;
231
+ text-transform: uppercase;
232
+ max-width: 250px;
233
+ height: 33px;
234
+ }
235
+ }
236
+
237
+ .button.loading {
238
+ background: white url('../../../images/opc-ajax-loader.gif') center center no-repeat;
239
+ background-size: 24px 24px;
240
+
241
+ span {
242
+ opacity: 0;
243
+ }
244
+ }
245
+ }
246
+
247
+ .doddle-sidebar-logo {
248
+ margin: 10px 0 8px 0;
249
+ width: 120px;
250
+ }
251
+
252
+
253
+ @media only screen and (max-width: 980px) {
254
+
255
+ #doddle-box {
256
+
257
+ table {
258
+
259
+ .more-information-td {
260
+
261
+ .more-information {
262
+ left: -440px;
263
+ right: 0;
264
+ }
265
+
266
+ }
267
+
268
+ }
269
+
270
+ }
271
+
272
+ }
273
+
274
+ @media only screen and (max-width: 680px) {
275
+
276
+ #doddle-box {
277
+ .doddle-search {
278
+
279
+ .doddle-search-button {
280
+ display: block;
281
+ width: 100%;
282
+ margin: 6px 0;
283
+ }
284
+
285
+ .doddle-or {
286
+ display: block;
287
+ width: 100%;
288
+ text-align: center;
289
+ }
290
+
291
+ .doddle-search-form {
292
+ display: block;
293
+ width: 100%;
294
+
295
+ .doddle-search-input {
296
+ margin: 6px 0 2px 0;
297
+ width: 100%;
298
+ display: block;
299
+ max-width: none;
300
+ }
301
+ }
302
+
303
+ }
304
+ }
305
+ }
skin/frontend/rwd/default/css/gene/doddle/fonts/doddle-icon.eot ADDED
Binary file
skin/frontend/rwd/default/css/gene/doddle/fonts/doddle-icon.svg ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <metadata>Generated by IcoMoon</metadata>
5
+ <defs>
6
+ <font id="icomoon" horiz-adv-x="1024">
7
+ <font-face units-per-em="1024" ascent="960" descent="-64" />
8
+ <missing-glyph horiz-adv-x="1024" />
9
+ <glyph unicode="&#x20;" d="" horiz-adv-x="512" />
10
+ <glyph unicode="&#x69;" d="M515.021 957.368c-282.767 0-512-229.233-512-512 0-282.757 229.233-511.99 512-511.99s512 229.233 512 511.99c0 282.767-229.233 512-512 512zM514.376 726.18c11.469 12.452 25.446 18.78 41.523 18.78 13.036 0 24.504-4.69 33.116-13.568 8.663-8.827 13.056-20.122 13.056-33.536 0-16.814-5.878-31.764-17.459-44.431-11.899-13.066-25.815-19.712-41.318-19.712-12.8 0-24.136 4.803-32.778 13.885-8.561 8.899-12.933 20.531-12.933 34.519 0 17.132 5.652 31.959 16.794 44.063zM599.665 243.804c-42.445-39.916-72.745-64.799-92.59-76.104-21.176-12.124-38.625-18.043-53.32-18.043-14.602 0-26.511 4.864-35.42 14.438-8.632 9.257-13.005 21.76-13.005 37.12 0 39.26 22.241 130.437 68.024 278.743 0.778 2.509 1.382 4.895 1.792 7.117-1.71-0.737-3.686-1.782-5.98-3.205-3.748-2.365-15.165-11.325-48.579-43.971-5.1-4.987-13.076-5.448-18.719-1.075l-19.763 15.288c-3.154 2.427-5.14 6.072-5.489 10.045-0.348 3.963 0.983 7.895 3.656 10.844 30.792 33.905 59.535 58.522 85.443 73.165 27.116 15.278 50.043 22.712 70.062 22.712 12.943 0 23.398-3.215 31.099-9.585 8.376-6.984 12.8-16.681 12.8-28.058 0-7.465-3.543-26.911-29.696-114.616-37.356-125.317-46.285-170.803-46.889-186.542 7.066 4.178 23.788 16.056 59.484 49.603 5.56 5.263 14.285 5.161 19.743-0.164l17.521-17.080c2.796-2.724 4.362-6.461 4.321-10.363s-1.659-7.598-4.495-10.271z" />
11
+ </font></defs></svg>
skin/frontend/rwd/default/css/gene/doddle/fonts/doddle-icon.ttf ADDED
Binary file
skin/frontend/rwd/default/css/gene/doddle/fonts/doddle-icon.woff ADDED
Binary file