Eabi_Livehandler - Version 0.1.1

Version Notes

Initial release

Download this release

Release Info

Developer Magento Core Team
Extension Eabi_Livehandler
Version 0.1.1
Comparing to
See all releases


Version 0.1.1

Files changed (47) hide show
  1. app/code/community/Eabi/Livehandler/Block/Adminhtml/Config/Form/Field/Button.php +57 -0
  2. app/code/community/Eabi/Livehandler/Block/Adminhtml/Config/Form/Field/Remove.php +89 -0
  3. app/code/community/Eabi/Livehandler/Block/Footer.php +166 -0
  4. app/code/community/Eabi/Livehandler/CHANGELOG.txt +3 -0
  5. app/code/community/Eabi/Livehandler/Helper/Data.php +172 -0
  6. app/code/community/Eabi/Livehandler/LICENCE.txt +1 -0
  7. app/code/community/Eabi/Livehandler/Model/Abstract.php +55 -0
  8. app/code/community/Eabi/Livehandler/Model/Action/Abstract.php +88 -0
  9. app/code/community/Eabi/Livehandler/Model/Adminhtml/Gridmanager.php +101 -0
  10. app/code/community/Eabi/Livehandler/Model/Entry.php +99 -0
  11. app/code/community/Eabi/Livehandler/Model/Mysql4/Entry.php +41 -0
  12. app/code/community/Eabi/Livehandler/Model/Mysql4/Entry/Collection.php +63 -0
  13. app/code/community/Eabi/Livehandler/Model/Ordergrid.php +814 -0
  14. app/code/community/Eabi/Livehandler/Model/System/Config/Backend/Button.php +127 -0
  15. app/code/community/Eabi/Livehandler/controllers/Adminhtml/LivehandlerController.php +109 -0
  16. app/code/community/Eabi/Livehandler/controllers/Adminhtml/RemoveController.php +67 -0
  17. app/code/community/Eabi/Livehandler/controllers/IndexController.php +102 -0
  18. app/code/community/Eabi/Livehandler/etc/config.xml +169 -0
  19. app/code/community/Eabi/Livehandler/etc/system.xml +107 -0
  20. app/code/community/Eabi/Livehandler/sql/eabi_livehandler_setup/mysql4-install-0.1.0.php +58 -0
  21. app/code/community/Eabi/Livehandler/sql/eabi_livehandler_setup/mysql4-upgrade-0.1.0-0.1.1.php +50 -0
  22. app/design/adminhtml/default/default/layout/eabi_livehandler.xml +32 -0
  23. app/design/frontend/base/default/layout/eabi_livehandler.xml +16 -0
  24. app/design/frontend/default/default/layout/eabi_livehandler.xml +16 -0
  25. app/etc/modules/Eabi_Livehandler.xml +21 -0
  26. app/locale/en_US/Eabi_Livehandler.csv +19 -0
  27. app/locale/et_EE/Eabi_Livehandler.csv +19 -0
  28. app/locale/fi_FI/Eabi_Livehandler.csv +19 -0
  29. app/locale/lt_LT/Eabi_Livehandler.csv +19 -0
  30. app/locale/sv_SE/Eabi_Livehandler.csv +19 -0
  31. js/eabi_js/crossBrowser_initKeyboardEvent.js +31 -0
  32. js/livepipe/contextmenu.js +185 -0
  33. js/livepipe/cookie.js +41 -0
  34. js/livepipe/event_behavior.js +298 -0
  35. js/livepipe/hotkey.js +74 -0
  36. js/livepipe/livepipe.js +181 -0
  37. js/livepipe/progressbar.js +102 -0
  38. js/livepipe/rating.js +156 -0
  39. js/livepipe/resizable.js +233 -0
  40. js/livepipe/scrollbar.js +227 -0
  41. js/livepipe/selection.js +459 -0
  42. js/livepipe/selectmultiple.js +155 -0
  43. js/livepipe/tabs.js +233 -0
  44. js/livepipe/textarea.js +122 -0
  45. js/livepipe/window.js +947 -0
  46. package.xml +22 -0
  47. skin/adminhtml/default/default/eabi_admintools.css +169 -0
app/code/community/Eabi/Livehandler/Block/Adminhtml/Config/Form/Field/Button.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Button
30
+ *
31
+ * @author Matis
32
+ */
33
+ class Eabi_Livehandler_Block_Adminhtml_Config_Form_Field_Button extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract {
34
+
35
+ public function __construct() {
36
+ $this->addColumn('button_name', array(
37
+ 'label' => Mage::helper('eabi_livehandler')->__('Button name'),
38
+ 'style' => 'width:120px',
39
+ 'class' => 'validate-code',
40
+ ));
41
+ $this->addColumn('sort_order', array(
42
+ 'label' => Mage::helper('adminhtml')->__('Sort order'),
43
+ 'style' => 'width:120px',
44
+ 'class' => 'validate-digits',
45
+ ));
46
+ $this->addColumn('disabled', array(
47
+ 'label' => Mage::helper('adminhtml')->__('Disabled'),
48
+ 'style' => 'width:120px',
49
+ 'class' => 'validate-digits-range digits-range-0-1',
50
+ ));
51
+ $this->_addAfter = false;
52
+ $this->_addButtonLabel = Mage::helper('eabi_livehandler')->__('Add button');
53
+ parent::__construct();
54
+ }
55
+
56
+ }
57
+
app/code/community/Eabi/Livehandler/Block/Adminhtml/Config/Form/Field/Remove.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Button
30
+ *
31
+ * @author Matis
32
+ */
33
+ class Eabi_Livehandler_Block_Adminhtml_Config_Form_Field_Remove extends Mage_Adminhtml_Block_System_Config_Form_Field {
34
+
35
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
36
+ $divId = $element->getId();
37
+ $helper = Mage::helper('eabi_livehandler');
38
+ $res = '';
39
+ $dirName = Mage::getBaseDir('code') . '/local/Eabi/Livehandler';
40
+ if (is_dir($dirName) && file_exists($dirName.'/etc/config.xml')) {
41
+ $res .= <<<HTML
42
+ <button class="scalable" id="{$divId}_button" type="button" onclick="{$divId}make_request(); return false;">{$helper->__('Delete instance of this module from %s folder', 'app/code/local')}</button>
43
+
44
+ HTML;
45
+ $res .= <<<HTML
46
+ <script type="text/javascript">
47
+ //<![CDATA[
48
+
49
+ function {$divId}make_request(actionName) {
50
+ var confirmR = confirm({$this->_toJson($helper->__('Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?', $dirName))});
51
+
52
+ if (confirmR) {
53
+ new Ajax.Request(
54
+ '{$this->getUrl('eabi_livehandler/adminhtml_remove/remove', array())}',
55
+ {
56
+ method: 'post',
57
+ asynchronous: true,
58
+ parameters: {"remove": "true"},
59
+ onSuccess: function(transport) {
60
+ var json = transport.responseText.evalJSON(true);
61
+ if (json['status'] && json['status'] == 'success') {
62
+ alert({$this->_toJson($helper->__('Folder %s deleted!', $dirName))});
63
+ \$({$this->_toJson($divId . '_button')}).hide();
64
+ } else {
65
+ alert({$this->_toJson($helper->__('Folder %s delete failed!', $dirName))});
66
+ }
67
+ },
68
+ onFailure: function(transport) {
69
+ alert(transport.responseText);
70
+ }
71
+ });
72
+ }
73
+
74
+ }
75
+ //]]>
76
+ </script>
77
+
78
+ HTML;
79
+
80
+ }
81
+ return $res;
82
+ }
83
+
84
+ private function _toJson($input) {
85
+ return json_encode($input);
86
+ }
87
+
88
+ }
89
+
app/code/community/Eabi/Livehandler/Block/Footer.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Footer
30
+ *
31
+ * @author matishalmann
32
+ */
33
+ class Eabi_LiveHandler_Block_Footer extends Mage_Core_Block_Template {
34
+ //put your code here
35
+
36
+
37
+ protected function _toHtml() {
38
+
39
+ $resultHtml = '';
40
+
41
+ if (!$this->_getEabi()->getConfigData('eabi_livehandler/main/enabled')) {
42
+ Mage::getSingleton('core/session')->unsetData('eabi_livehandler_entries');
43
+ return $resultHtml;
44
+ }
45
+
46
+ $isAdmin = false;
47
+ $urlKey = 'eabi_livehandler/index/process';
48
+ if (Mage::app()->getStore()->isAdmin()) {
49
+ $isAdmin = true;
50
+ $urlKey = 'eabi_livehandler/adminhtml_livehandler/process';
51
+ }
52
+ $path = '';
53
+ $website = Mage::app()->getStore()->getWebsiteId();
54
+ $store = Mage::app()->getStore()->getStoreId();
55
+ $request = Mage::app()->getRequest();
56
+
57
+ Mage::getSingleton('core/session')->unsetData('eabi_livehandler_entries');
58
+
59
+ if ($isAdmin) {
60
+ if ($request->getParam('store')) {
61
+ $store = Mage::getModel('core/store')->load($request->getParam('store'))->getId();
62
+ }
63
+ if ($request->getParam('website')) {
64
+ $website = Mage::getModel('core/website')->load($request->getParam('website'))->getId();
65
+ }
66
+
67
+ }
68
+
69
+ $routeName = $request->getRequestedRouteName();
70
+ $controllerName = $request->getRequestedControllerName();
71
+ $actionName = $request->getRequestedActionName();
72
+ $path = $routeName . '/' . $controllerName . '/' . $actionName;
73
+ // $resultHtml .= $path . '/' . $website . '/' . $store;
74
+
75
+
76
+ $actionsCollection = Mage::getModel('eabi_livehandler/entry')->getCollection()->setFilter($path, $isAdmin, $website, $store);
77
+ $lastRequestVar = null;
78
+ $allowedActions = array();
79
+ foreach ($actionsCollection as $id => $action) {
80
+ $suppliedParams = array();
81
+ $action->load($action->getId());
82
+ $targetUrl = '';
83
+ $css = '';
84
+ $js = '';
85
+ $html = '';
86
+ if ($action->getData('request_var') !== $lastRequestVar || true) {
87
+ //if we do have same path and defined many actions, then only the first one in the path will be executed.
88
+
89
+ $js = trim($action->getJs());
90
+ $css = trim($action->getCss());
91
+ $html = trim($action->getHtml());
92
+
93
+ if ($action->getData('model_class') != '') {
94
+ $allowedActions[$action->getData('model_class')] = time();
95
+ }
96
+ $params['__path'] = base64_encode($action->getData('model_class'));
97
+
98
+ if ($isAdmin) {
99
+ $targetUrl = Mage::helper('adminhtml')->getUrl($urlKey, $params);
100
+ } else {
101
+ $targetUrl = $this->getUrl($urlKey, $params);
102
+ }
103
+
104
+ $lastRequestVar = $action->getData('request_var');
105
+
106
+ if ($html != '') {
107
+ $resultHtml .= $html;
108
+ }
109
+ if ($css != '') {
110
+ $resultHtml .= <<<EOT
111
+ <style type="text/css">
112
+ {$css}
113
+ </style>
114
+ EOT;
115
+ }
116
+ if ($js != '') {
117
+ $urlString = '';
118
+ if ($targetUrl != '') {
119
+
120
+ $urlString = <<<EOT
121
+ var action_url = '{$targetUrl}';
122
+ EOT;
123
+ }
124
+ $resultHtml .= <<<EOT
125
+ <script type="text/javascript">
126
+ //<![CDATA[
127
+ (function() {
128
+ {$urlString}
129
+
130
+ {$js}
131
+ })();
132
+ //]]>
133
+ </script>
134
+ EOT;
135
+ }
136
+ }
137
+
138
+ //execute each action and supply parameters
139
+
140
+ //execute makes the following
141
+ /*
142
+ * echo the target URL for the proper action handler
143
+ * echo action handlers CSS
144
+ * echo action handlers JS
145
+ * echo action handler HTML
146
+ *
147
+ * action url would have to execute the correct action
148
+ *
149
+ *
150
+ *
151
+ */
152
+ }
153
+ Mage::getSingleton('core/session')->setData('eabi_livehandler_entries', $allowedActions);
154
+ return $resultHtml;
155
+ }
156
+
157
+
158
+ /**
159
+ *
160
+ * @return Eabi_Livehandler_Helper_Data
161
+ */
162
+ protected function _getEabi() {
163
+ return Mage::helper('eabi');
164
+ }
165
+ }
166
+
app/code/community/Eabi/Livehandler/CHANGELOG.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ 0.1.1
2
+ - Initial release
3
+
app/code/community/Eabi/Livehandler/Helper/Data.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * <p>Handles saving and loading Magento's core_config_data values.</p>
30
+ * <p>Can save and load PHP objects to core_config_data table.</p>
31
+ *
32
+ * @author Aktsiamaailm OÜ, Matis Halmann
33
+ */
34
+ class Eabi_Livehandler_Helper_Data extends Mage_Core_Helper_Abstract {
35
+
36
+
37
+ /**
38
+ * <p>Behaves similar to Mage::getStoreConfig() method, but returns PHP objects instead of strings</p>
39
+ * @param string $key configuration key to fetch
40
+ * @param string|bool $default default value to return if the value does not exist or unserialization failed
41
+ * @param int $storeId store id, to fetch the configuration value for
42
+ * @return object|array|string
43
+ */
44
+ final public function getBigConfigData($key, $default = false, $storeId = null) {
45
+ $i = 0;
46
+
47
+ $selfValue = $this->getConfigData($key, $default, $storeId);
48
+ while (strlen($loadedString = $this->getConfigData($key . $i, $default, $storeId)) > 0) {
49
+ if (!is_string($loadedString)) {
50
+ break;
51
+ }
52
+ $selfValue .= $loadedString;
53
+ $i++;
54
+ }
55
+
56
+ $finalValue = @unserialize(@gzuncompress(@base64_decode($selfValue)));
57
+
58
+ if (!is_array($finalValue)) {
59
+ return $default;
60
+ }
61
+ return $finalValue;
62
+ }
63
+
64
+ /**
65
+ * <p>Stores PHP object to core_config_data table using object serialization.</p>
66
+ * <p>Save procedure of stored object:
67
+ * <ul>
68
+ <li>serialize php object</li>
69
+ <li>gzcompress the serialized result</li>
70
+ <li>base64 encode the compressed result</li>
71
+ <li>If the compressed result is larger than 64K, then the resulting blocks will be saved under $key . $i ID, where $i starts with 0 and is incremented by 1 for each consecutive 64K block.</li>
72
+ </ul>
73
+ * </p>
74
+ * @param string $key configuration key to store this value
75
+ * @param object|array|string $value object to store into the configuration
76
+ * @param string $scope Magento's configuration scope
77
+ * @param int $scopeId store ID this configuration will be saved to
78
+ * @param bool $skipFirst if this setting is true, then first segment will not be saved to database using this function. Useful, when overriding Mage_Core_Model_Config_Data saving functions.
79
+ * @return string first saved segment of this saved configuration data.
80
+ */
81
+ final public function setBigConfigData($key, $value, $scope = 'default', $scopeId = 0, $skipFirst = false) {
82
+ $strValue = base64_encode(gzcompress(serialize($value)));
83
+ $strValues = str_split($strValue, 64000);
84
+ $cnt = 0;
85
+
86
+ $firstValue = array_shift($strValues);
87
+ if (!$skipFirst) {
88
+ $this->setConfigData($key, $firstValue, $scope, $scopeId, false);
89
+ }
90
+
91
+ foreach ($strValues as $strVal) {
92
+ $this->setConfigData($key . $cnt, $strVal, $scope, $scopeId, false);
93
+ $cnt ++;
94
+ }
95
+ $this->setConfigData($key . $cnt, '', $scope, $scopeId, true);
96
+ return $firstValue;
97
+ }
98
+
99
+
100
+ /**
101
+ * <p>Behaves similar to Mage::getStoreConfig() but attempts to return the resulting value as float.</p>
102
+ * <p>Supports "." and "," as decimal separator</p>
103
+ * @param string $key configuration key to load
104
+ * @param bool|object $default default value to return if the configuration value does not exist.
105
+ * @param int $storeId store id to load the configuration value for.
106
+ * @return float
107
+ */
108
+ final public function getConfigDataF($key, $default = false, $storeId = null) {
109
+ return $this->getConfigData($key, $default, $storeId, true);
110
+ }
111
+
112
+ /**
113
+ * <p>Behaves similar to Mage::getStoreConfig() but returns array of elements, where each element is one line of data contained. Empty lines are not returned.</p>
114
+ * @param string $key configuration key to load
115
+ * @param bool|object $default default value to return if the configuration value does not exist.
116
+ * @param int $storeId store id to load the configuration value for.
117
+ * @return array
118
+ */
119
+ final public function getConfigDataA($key, $default = false, $storeId = null) {
120
+ $value = $this->getConfigData($key, $default, $storeId, false);
121
+ if (is_string($value)) {
122
+ $rawModules = explode("\n", $value);
123
+ return array_filter(array_map('trim', $rawModules));
124
+ }
125
+ return $value;
126
+ }
127
+
128
+ /**
129
+ * <p>Behaves similar to Mage::getStoreConfig() function and can return the value as float.</p>
130
+ * @param string $key configuration key to load
131
+ * @param bool|object $default default value to return if the configuration value does not exist.
132
+ * @param int $storeId store id to load the configuration value for.
133
+ * @param bool $asFloat when true, then attemts to return the value as float
134
+ * @return string
135
+ */
136
+ final public function getConfigData($key, $default = false, $storeId = null, $asFloat = false) {
137
+ if ($storeId === null) {
138
+ $storeId = Mage::app()->getStore()->getId();
139
+ }
140
+ $value = Mage::getStoreConfig($key, $storeId);
141
+ if (is_null($value) || false === $value) {
142
+ $value = $default;
143
+ }
144
+ if ($asFloat) {
145
+ $value = str_replace(',', '.', $value);
146
+ }
147
+ return $value;
148
+ }
149
+
150
+ /**
151
+ * <p>Writes the core_config_data value to database and resets the cache.</p>
152
+ * @param string $key Identification path to write the configuarion to.
153
+ * @param string $value Value to store
154
+ * @param string $scope Magento's scope to apply
155
+ * @param int $scopeId Store ID to apply
156
+ * @param bool $resetCache If the cache should be reset after saving the configuration value.
157
+ * @return Eabi_Livehandler_Helper_Data
158
+ */
159
+ final public function setConfigData($key, $value, $scope = 'default', $scopeId = 0, $resetCache = true) {
160
+ $config = Mage::getConfig();
161
+ $config->saveConfig($key, $value, $scope, $scopeId);
162
+ if ($resetCache) {
163
+ $config->cleanCache();
164
+ }
165
+ return $this;
166
+ }
167
+
168
+
169
+
170
+ }
171
+
172
+
app/code/community/Eabi/Livehandler/LICENCE.txt ADDED
@@ -0,0 +1 @@
 
1
+ http://opensource.org/licenses/osl-3.0.php
app/code/community/Eabi/Livehandler/Model/Abstract.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Abstract
30
+ *
31
+ * @author matishalmann
32
+ */
33
+ abstract class Eabi_Livehandler_Model_Abstract extends Mage_Core_Model_Abstract {
34
+ //put your code here
35
+
36
+ //construct will be done in subclasses
37
+
38
+ public function getJs() {
39
+ return '';
40
+ }
41
+
42
+ public function getCss() {
43
+ return '';
44
+ }
45
+
46
+ public function getHtml() {
47
+ return '';
48
+ }
49
+
50
+ public function process($postedData) {
51
+ return array();
52
+ }
53
+
54
+ }
55
+ ?>
app/code/community/Eabi/Livehandler/Model/Action/Abstract.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Abstract
30
+ *
31
+ * @author Matis
32
+ */
33
+ abstract class Eabi_Livehandler_Model_Action_Abstract {
34
+ protected $_position;
35
+ protected $_code;
36
+ protected $_label;
37
+ protected $_onClick;
38
+ protected $_longOnClick;
39
+ protected $_cssClass;
40
+
41
+ /**
42
+ * Decides whether the button can be displayed for the current order
43
+ */
44
+ abstract public function canDisplay(Mage_Sales_Model_Order $order);
45
+
46
+ /**
47
+ * Performs the desired action, when button can be displayed and the button itself is clicked
48
+ * Should return:
49
+ * - messages
50
+ * - errors
51
+ * - any other data
52
+ *
53
+ */
54
+ abstract public function performDesiredAction(Mage_Sales_Model_Order $order, array $params);
55
+
56
+
57
+ public function setPosition($position) {
58
+ $this->_position = $position;
59
+ }
60
+ public function getPosition() {
61
+ return $this->_position;
62
+ }
63
+
64
+ public function getCode() {
65
+ return $this->_code;
66
+ }
67
+
68
+ public function getLabel() {
69
+ return $this->_label;
70
+ }
71
+
72
+ public function getOnClick() {
73
+ return $this->_onClick;
74
+ }
75
+ public function getCssClass() {
76
+ return $this->_cssClass;
77
+ }
78
+ public function getLongOnClick() {
79
+ return $this->_longOnClick;
80
+ }
81
+
82
+
83
+ protected function _toJson($input) {
84
+ return json_encode($input);
85
+ }
86
+
87
+ }
88
+
app/code/community/Eabi/Livehandler/Model/Adminhtml/Gridmanager.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Gridmanager
30
+ *
31
+ * @author matishalmann
32
+ */
33
+ class Eabi_Livehandler_Model_Adminhtml_Gridmanager extends Eabi_Livehandler_Model_Abstract {
34
+
35
+ protected $_actionButtons = array();
36
+ //put your code here
37
+ public function _construct() {
38
+ parent::_construct();
39
+ $this->_init('eabi_livehandler/adminhtml_gridmanager');
40
+ }
41
+
42
+ public function addActionButton($id, $title, $onclick) {
43
+ $this->_actionButtons[$id] = array(
44
+ 'title' => $title,
45
+ 'onclick' => $onclick,
46
+ );
47
+ return $this;
48
+ }
49
+
50
+ public function removeActionButton($id) {
51
+ if (isset($this->_actionButtons[$id])) {
52
+ unset($this->_actionButtons[$id]);
53
+ }
54
+ return $this;
55
+ }
56
+
57
+ /**
58
+ * Encodes input to JSON
59
+ * @param mixed $input
60
+ * @return string
61
+ */
62
+ protected function _encode($input) {
63
+ return json_encode($input);
64
+
65
+ }
66
+
67
+ public function getJs() {
68
+ $js = '';
69
+
70
+ foreach ($this->_actionButtons as $id => $actionButton) {
71
+ $onclick = str_replace('\"','"',addslashes($actionButton['onclick']));
72
+ $title = str_replace('\"','"',addslashes($actionButton['title']));
73
+ $js .= <<<EOT
74
+ var button_{$id} = new Element('button');
75
+ button_{$id}.writeAttribute('id', '{$id}');
76
+ button_{$id}.writeAttribute('type', 'button');
77
+ button_{$id}.writeAttribute('class', 'scalable');
78
+ button_{$id}.writeAttribute('onclick', '{$onclick}');
79
+ button_{$id}.update('<span>{$title}</span>');
80
+
81
+ $$('p.form-buttons').first().insert(button_{$id}, { position: 'bottom'});
82
+
83
+ EOT;
84
+ }
85
+
86
+ $js .= $this->_getAdditionalJs($js);
87
+
88
+ return $js;
89
+ }
90
+
91
+ protected function _getAdditionalJs($currentJs) {
92
+ return '';
93
+ }
94
+
95
+
96
+
97
+ public function service($postData) {
98
+ return array();
99
+ }
100
+ }
101
+
app/code/community/Eabi/Livehandler/Model/Entry.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Entry
30
+ *
31
+ * @author matishalmann
32
+ */
33
+ class Eabi_Livehandler_Model_Entry extends Mage_Core_Model_Abstract {
34
+
35
+ /**
36
+ *
37
+ * @var Eabi_Livehandler_Model_Adminhtml_Gridmanager
38
+ */
39
+ private $_caller;
40
+
41
+ public function _construct() {
42
+ parent::_construct();
43
+ $this->_init('eabi_livehandler/entry');
44
+
45
+ }
46
+
47
+ protected function _afterLoad() {
48
+ $load = parent::_afterLoad();
49
+ if ($this->getData('model_class') != '') {
50
+ $this->_caller = Mage::getModel($this->getData('model_class'));
51
+ }
52
+ return $load;
53
+ }
54
+
55
+ public function getJs() {
56
+ $js = trim($this->getData('js'));
57
+ if ($js == '' && $this->_caller != null) {
58
+ $js = $this->_caller->getJs();
59
+ }
60
+
61
+ return $js;
62
+
63
+ }
64
+
65
+ public function getCss() {
66
+ $css = trim($this->getData('css'));
67
+ if ($css == '' && $this->_caller != null) {
68
+ $css = $this->_caller->getCss();
69
+ }
70
+
71
+ return $css;
72
+
73
+ }
74
+
75
+ public function getHtml() {
76
+ $html = trim($this->getData('html'));
77
+ if ($html == '' && $this->_caller != null) {
78
+ $html = $this->_caller->getHtml();
79
+ }
80
+
81
+ return $html;
82
+
83
+ }
84
+
85
+ public function runPublic($postedData) {
86
+ $result = array();
87
+ if ($this->_caller != null) {
88
+ $result = $this->_caller->service($postedData);
89
+ }
90
+ return $result;
91
+ }
92
+ public function runAdmin($postedData) {
93
+ $result = array();
94
+ if ($this->_caller != null) {
95
+ $result = $this->_caller->service($postedData);
96
+ }
97
+ return $result;
98
+ }
99
+ }
app/code/community/Eabi/Livehandler/Model/Mysql4/Entry.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Entry
30
+ *
31
+ * @author matishalmann
32
+ */
33
+ class Eabi_Livehandler_Model_Mysql4_Entry extends Mage_Core_Model_Mysql4_Abstract {
34
+ //put your code here
35
+
36
+ public function _construct() {
37
+ $this->_init('eabi_livehandler/entry', 'id');
38
+ }
39
+ }
40
+
41
+
app/code/community/Eabi/Livehandler/Model/Mysql4/Entry/Collection.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Collection
30
+ *
31
+ * @author matishalmann
32
+ */
33
+ class Eabi_Livehandler_Model_Mysql4_Entry_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
34
+ //put your code here
35
+ public function _construct() {
36
+ parent::_construct();
37
+ $this->_init('eabi_livehandler/entry');
38
+ }
39
+
40
+ public function setFilter($path, $isAdmin = false, $website = 0, $store = 0) {
41
+ $this->addFieldToFilter('request_var', array('like' => $path.'%'));
42
+ $this->addFieldToFilter('is_admin', (int)$isAdmin);
43
+ $this->addFieldToFilter('is_enabled', 1);
44
+ $this->getSelect()->where('main_table.website_id = '.((int)$website).' or main_table.website_id = 0');
45
+ $this->getSelect()->where('main_table.store_id = '.((int)$store).' or main_table.store_id = 0');
46
+ $this->addOrder('request_var', 'asc');
47
+ $this->addOrder('website_id', 'desc');
48
+ $this->addOrder('store_id', 'desc');
49
+ return $this;
50
+ }
51
+ public function setModelFilter($model, $isAdmin = false, $website = 0, $store = 0) {
52
+ $this->addFieldToFilter('model_class', $model);
53
+ $this->addFieldToFilter('is_admin', (int)$isAdmin);
54
+ $this->addFieldToFilter('is_enabled', 1);
55
+ $this->getSelect()->where('main_table.website_id = '.((int)$website).' or main_table.website_id = 0');
56
+ $this->getSelect()->where('main_table.store_id = '.((int)$store).' or main_table.store_id = 0');
57
+ $this->addOrder('request_var', 'asc');
58
+ $this->addOrder('website_id', 'desc');
59
+ $this->addOrder('store_id', 'desc');
60
+ return $this;
61
+ }
62
+ }
63
+ ?>
app/code/community/Eabi/Livehandler/Model/Ordergrid.php ADDED
@@ -0,0 +1,814 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Ordergrid
30
+ *
31
+ * @author matishalmann
32
+ */
33
+ class Eabi_Livehandler_Model_Ordergrid extends Eabi_Livehandler_Model_Adminhtml_Gridmanager {
34
+
35
+ private static $_allowedActions;
36
+ private static $_hasButtons = false;
37
+ CONST CACHE_KEY = 'eabi_livehandler_model_ordergrid';
38
+
39
+ protected $_loadedButtons = array();
40
+
41
+
42
+ public function _construct() {
43
+ parent::_construct();
44
+ $this->_init('eabi_livehandler/ordergrid');
45
+ $this->_initButtons();
46
+ }
47
+
48
+ private function _initButtons() {
49
+ if (self::$_allowedActions === null) {
50
+ //first check the folder model/actions if the checked result is not already stored in cache
51
+ //effectively each time cache is cleared existense for new button classes is checked
52
+ //if cache is disabled entirely then each load time existense of the button classes is checked when
53
+ //order detail view screen is loaded
54
+ $cache = Mage::app()->getCache();
55
+ /* @var $cache Mage_Core_Model_Cache */
56
+ $configStored = $cache->load(self::CACHE_KEY);
57
+ if ($configStored != 'true') {
58
+ //reload the config
59
+ /* @var $configBackend Eabi_Livehandler_Model_System_Config_Backend_Button */
60
+ $configBackend = Mage::getModel('eabi_livehandler/system_config_backend_button');
61
+ $configBackend->load('eabi_livehandler/admintools/buttons', 'path');
62
+ $configBackend->save();
63
+ if ($configBackend->isValueChanged() && is_array($configBackend->getValue())) {
64
+ $this->getEabi()->setConfigData('eabi_livehandler/admintools/buttons', serialize($configBackend->getValue()));
65
+ }
66
+
67
+ //since the actual configuration is saved in core_config_data, which is cached anyway
68
+ //then we only need a mark that we checked for the existence
69
+ //put lifetime=forever, because new check should be performed only when cache is cleared
70
+ //cache tag should be configuration, because we hold the actual value in Magentos configuration
71
+ $cache->save('true', self::CACHE_KEY, array(Mage_Core_Model_Config::CACHE_TAG), 0);
72
+
73
+ }
74
+
75
+ //read the available button classes from the configuration
76
+ //because detecting the buttons from the folder is slow, that is why only configuration is checked.
77
+ $allowedActions = @unserialize(Mage::getStoreConfig('eabi_livehandler/admintools/buttons'));
78
+ if (!is_array($allowedActions)) {
79
+ //we have no buttons, or the saved configuration was garbage
80
+ $allowedActions = array();
81
+ }
82
+ self::$_allowedActions = array();
83
+ //sort the allowed actions by sort_order
84
+ uasort($allowedActions, array(__CLASS__, '_sortAction'));
85
+ foreach ($allowedActions as $allowedAction) {
86
+ //exclude disabled button classes
87
+ if (!(bool)$allowedAction['disabled']) {
88
+ self::$_allowedActions[] = $allowedAction['button_name'];
89
+ }
90
+ self::$_hasButtons = true;
91
+ }
92
+ }
93
+ }
94
+
95
+
96
+ public static function _sortAction($a, $b) {
97
+ if ((bool)$a['disabled'] && !(bool)$b['disabled']) {
98
+ return 1;
99
+ }
100
+ if (!(bool)$a['disabled'] && (bool)$b['disabled']) {
101
+ return -1;
102
+ }
103
+ if ((int)$a['sort_order'] == (int)$b['sort_order']) {
104
+ return 0;
105
+ }
106
+ return (int)$a['sort_order'] < (int)$b['sort_order']?-1:1;
107
+ }
108
+
109
+ /**
110
+ * Returns result for the ajax call for this action.
111
+ * Parameters it the postdata itself.
112
+ * If this module is disabled, then assoc array of
113
+ * array('result' => false); is returned.
114
+ *
115
+ * @param array $postData
116
+ * @return array
117
+ */
118
+ public function service($postData) {
119
+ $errors = array();
120
+ $messages = array();
121
+
122
+ $result = array(
123
+ 'success' => false,
124
+ 'set_location' => false,
125
+ );
126
+ if (!Mage::getStoreConfig('eabi_livehandler/admintools/enabled')) {
127
+ return $result;
128
+ }
129
+ if (isset($postData['order_id'])) {
130
+ $order = Mage::getModel('sales/order')->load($postData['order_id']);
131
+ if ($order->getId() > 0) {
132
+ $isActionError = false;
133
+
134
+
135
+ //send_email
136
+ //unhold
137
+ //hold
138
+ //cancel
139
+ //invoice
140
+ if (isset($postData['action']) && in_array($postData['action'], self::$_allowedActions)) {
141
+ $actionModel = Mage::getSingleton('eabi_livehandler/action_'.$postData['action']);
142
+ /* @var $actionModel Eabi_Livehandler_Model_Action_Abstract */
143
+ if ($actionModel && $actionModel instanceof Eabi_Livehandler_Model_Action_Abstract
144
+ && $actionModel->canDisplay($order)) {
145
+ $data = isset($postData['extra_data'])?json_decode($postData['extra_data'], true):array();
146
+ if (!is_array($data)) {
147
+ $data = array();
148
+ }
149
+ $actionResult = $actionModel->performDesiredAction($order, $data);
150
+ if (isset($actionResult['errors'])) {
151
+ $errors = array_merge($messages, $actionResult['errors']);
152
+ }
153
+ $messages = array_merge($messages, $actionResult['messages']);
154
+ $isActionError = $actionResult['is_action_error'];
155
+ $result['needs_reload'] = $actionResult['needs_reload'];
156
+ if (isset($actionResult['set_location'])) {
157
+ $result['set_location'] = $actionResult['set_location'];
158
+ }
159
+ } else {
160
+ $isActionError = true;
161
+ }
162
+ } else {
163
+ // $isActionError = true;
164
+ }
165
+ if ($isActionError) {
166
+ $errors[] = Mage::helper('eabi_livehandler')->__('Invalid action');
167
+ $result['needs_reload'] = true;
168
+ }
169
+ if ($result['set_location'] && strlen($result['set_location'])> 3) {
170
+ return $result;
171
+ }
172
+
173
+ $address = $order->getShippingAddress();
174
+ if (!$address) {
175
+ $address = $order->getBillingAddress();
176
+ }
177
+
178
+ $result['_order'] = $order->debug();
179
+ $result['address'] = $address->format('html');
180
+ $result['customer_email'] = htmlspecialchars($order->getCustomerEmail());
181
+ $result['total_paid'] = htmlspecialchars(Mage::helper('core')->currency($order->getBaseTotalPaid(), true, false));
182
+ $result['grand_total'] = htmlspecialchars(Mage::helper('core')->currency($order->getBaseGrandTotal(), true, false));
183
+ $result['base_shipping'] = htmlspecialchars(Mage::helper('core')->currency($order->getBaseShippingAmount(), true, false));
184
+ $result['base_total'] = htmlspecialchars(Mage::helper('core')->currency($order->getBaseGrandTotal() - $order->getBaseShippingAmount(), true, false));
185
+ $result['base_subtotal_to_refund'] = htmlspecialchars(Mage::helper('core')->currency($order->getBaseSubtotalInvoiced() - $order->getBaseSubtotalRefunded(), true, false));
186
+ $result['base_total_to_refund'] = htmlspecialchars(Mage::helper('core')->currency($order->getBaseTotalInvoiced() - $order->getBaseTotalRefunded(), true, false));
187
+ $result['payment'] = htmlspecialchars($order->getPayment()->getMethodInstance()->getTitle());
188
+ $result['shipping'] = htmlspecialchars($order->getShippingDescription());
189
+ if ($result['shipping'] == '') {
190
+ $result['shipping'] = Mage::helper('eabi_livehandler')->__('Not applicable');
191
+ }
192
+ $result['status_label'] = htmlspecialchars($order->getStatusLabel());
193
+ $result['discount_description'] = htmlspecialchars($order->getDiscountDescription());
194
+ if ($order->getBaseDiscountAmount() > 0) {
195
+ $result['discount_amount'] = htmlspecialchars(Mage::helper('core')->currency($order->getBaseDiscountAmount() * -1, true, false));
196
+
197
+ } else {
198
+ $result['discount_amount'] = htmlspecialchars(Mage::helper('core')->currency($order->getBaseDiscountAmount(), true, false));
199
+
200
+ }
201
+ $orderItems = $order->getAllVisibleItems();
202
+ $products = array();
203
+ foreach ($orderItems as $orderItem) {
204
+
205
+ // $products[] = $orderItem->debug() + array('prod_options' => $orderItem->getProductOptions());
206
+
207
+ $html = <<<EOT
208
+ <tr>
209
+ <td class="a-left">%name%</td>
210
+ <td>%sku%</td>
211
+ <td>%qty%</td>
212
+ <td>%price%</td>
213
+ <td>%total%</td>
214
+ </tr>
215
+
216
+ EOT;
217
+ $name = htmlspecialchars($orderItem->getName());
218
+ $options = $orderItem->getProductOptions();
219
+ $option = '';
220
+ if (isset($options['options'])) {
221
+ foreach ($options['options'] as $optionValues) {
222
+ if ($optionValues['value']) {
223
+ $option .= '<br/><strong><i>'. htmlspecialchars($optionValues['label']).'</i></strong>: ';
224
+ $_printValue = isset($optionValues['print_value']) ? $optionValues['print_value'] : strip_tags($optionValues['value']);
225
+ $values = explode(', ', $_printValue);
226
+ foreach ($values as $value) {
227
+ if (is_array($value)) {
228
+ foreach ($value as $_value) {
229
+ $option .= htmlspecialchars($_value);
230
+ }
231
+ } else {
232
+ $option .= htmlspecialchars($value);
233
+ }
234
+ $option .= '<br/>';
235
+ }
236
+ }
237
+ }
238
+ }
239
+ $name .= $option;
240
+ $basePrice = $orderItem->getBasePriceInclTax();
241
+ if ($basePrice == null) {
242
+ $basePrice = $orderItem->getBasePrice();
243
+ }
244
+ $toReplace = array(
245
+ 'name' => $name,
246
+ 'sku' => htmlspecialchars($orderItem->getSku()),
247
+ 'qty' => round($orderItem->getQtyOrdered()),
248
+ 'price' => Mage::helper('core')->currency($basePrice, true, false),
249
+ 'total' => Mage::helper('core')->currency($basePrice * $orderItem->getQtyOrdered(), true, false),
250
+ );
251
+
252
+ foreach ($toReplace as $key => $value) {
253
+ $html = str_replace('%'.$key.'%', $value, $html);
254
+ }
255
+
256
+ $products[] = array(
257
+ 'name' => $orderItem->getName(),
258
+ 'base_original_price' => $orderItem->getBaseOriginalPrice(),
259
+ 'base_price' => $orderItem->getBasePrice(),
260
+ 'base_price_incl_tax' => $orderItem->getBasePriceInclTax(),
261
+ 'original_price' => $orderItem->getOriginalPrice(),
262
+ 'price' => $orderItem->getPrice(),
263
+ 'price_incl_tax' => $orderItem->getPriceInclTax(),
264
+ 'product_id' => $orderItem->getProductId(),
265
+ 'product_options' => $orderItem->getProductOptions(),
266
+ 'qty_ordered' => $orderItem->getQtyOrdered(),
267
+ 'row_total' => $orderItem->getRowTotal(),
268
+ 'row_total_incl_tax' => $orderItem->getRowTotalInclTax(),
269
+ 'sku' => $orderItem->getSku(),
270
+ 'tax_amount' => $orderItem->getTaxAmount(),
271
+ 'tax_percent' => $orderItem->getTaxPercent(),
272
+ 'html' => $html,
273
+ );
274
+
275
+ }
276
+
277
+ $buttonsHtml = '';
278
+ if (!self::$_hasButtons) {
279
+ $buttonsHtml .= '<span class="eabi_commercial">'.$this->getEabi()->__('Manage the order from here by adding <a href=\'%s\' target=\'_blank\'>action buttons</a>', $this->getEabi()->__($this->getEabi()->getConfigData('eabi_livehandler/admintools/buttons_url'))).'</span>';
280
+ }
281
+
282
+ foreach (self::$_allowedActions as $allowedAction) {
283
+ $actionButtonModel = Mage::getSingleton('eabi_livehandler/action_'.$allowedAction);
284
+ /* @var $actionButtonModel Eabi_Livehandler_Model_Action_Abstract */
285
+ if ($actionButtonModel && $actionButtonModel instanceof Eabi_Livehandler_Model_Action_Abstract
286
+ && $actionButtonModel->canDisplay($order)) {
287
+ $buttonsHtml .= $this->__makeActionButton($actionButtonModel->getLabel(), $actionButtonModel->getOnClick(), $actionButtonModel->getCssClass(), 'eabi_'.$actionButtonModel->getCode());
288
+ $this->_loadedButtons = $actionButtonModel;
289
+
290
+ }
291
+ }
292
+
293
+
294
+ /* @var $adminHelper Mage_Adminhtml_Helper_Data */
295
+ $adminHelper = Mage::helper('adminhtml');
296
+ $result['success'] = true;
297
+ $result['header_html'] = <<<EOT
298
+ <div class="grid products_grid">
299
+ <table><thead>
300
+ <tr class="headings">
301
+ <th>{$adminHelper->__('Product')}</th>
302
+ <th>{$adminHelper->__('SKU')}</th>
303
+ <th>{$adminHelper->__('Qty')}</th>
304
+ <th>{$adminHelper->__('Price')}</th>
305
+ <th>{$adminHelper->__('Row Total')}</th>
306
+ </tr>
307
+ </thead>
308
+ <tbody>
309
+ EOT;
310
+ $result['footer_html'] = <<<EOT
311
+ </tbody>
312
+ </table>
313
+ </div>
314
+ EOT;
315
+
316
+ $result['customer_address_html'] = '';
317
+
318
+ if (count($errors) > 0 || count($messages) > 0) {
319
+ $result['customer_address_html'] .= '<ul class="messages">';
320
+
321
+ if (count($messages) > 0) {
322
+ $result['customer_address_html'] .= '<li class="success-msg"><ul>';
323
+ foreach ($messages as $message) {
324
+ $result['customer_address_html'] .= '<li>'. htmlspecialchars($message).'</li>';
325
+ }
326
+ $result['customer_address_html'] .= '</ul></li>';
327
+ }
328
+
329
+ if (count($errors) > 0) {
330
+ $result['customer_address_html'] .= '<li class="error-msg"><ul>';
331
+ foreach ($errors as $error) {
332
+ $result['customer_address_html'] .= '<li>'. htmlspecialchars($error).'</li>';
333
+ }
334
+ $result['customer_address_html'] .= '</ul></li>';
335
+ }
336
+
337
+ $result['customer_address_html'] .= '</ul>';
338
+ }
339
+ $_blank = '';
340
+ if (Mage::getStoreConfig('eabi_livehandler/admintools/open_in_new')) {
341
+ $_blank = ' target="_blank"';
342
+ }
343
+ $orderUrl = Mage::helper('adminhtml')->getUrl('adminhtml/sales_order/view', array('order_id' => $order->getId()));
344
+ $result['customer_address_html'] .= <<<EOT
345
+ <div class="grid detail_grid">
346
+ <table><thead>
347
+ <tr class="headings">
348
+ <th>{$result['total_paid']}/{$result['grand_total']}</th>
349
+ <th class="a-right"><div class="eabi_buttons">{$buttonsHtml}</div>&nbsp;<a href="{$orderUrl}" title="{$adminHelper->__('View')}" {$_blank}>{$result['status_label']}</a></strong></th>
350
+ </tr>
351
+ </thead>
352
+ <tbody>
353
+ <tr>
354
+ <td class="a-left"><strong><a href="mailto:{$result['customer_email']}">{$result['customer_email']}</a></strong><br/>{$result['address']}</td>
355
+ <td>
356
+ <table>
357
+ <thead>
358
+ <tr>
359
+ <th>{$adminHelper->__('Payment Information')}</th>
360
+ </tr>
361
+ </thead>
362
+ <tbody>
363
+ <tr>
364
+ <td>{$result['payment']}</td>
365
+ </tr>
366
+ </tbody>
367
+ </table>
368
+ <table>
369
+ <thead>
370
+ <tr>
371
+ <th>{$adminHelper->__('Shipping &amp; Handling Information')}</th>
372
+ </tr>
373
+ </thead>
374
+ <tbody>
375
+ <tr>
376
+ <td>{$result['shipping']}</td>
377
+ </tr>
378
+ </tbody>
379
+ </table>
380
+ </td>
381
+ </tr>
382
+ </tbody>
383
+ </table>
384
+ </div>
385
+
386
+ EOT;
387
+
388
+ if ($order->getBaseDiscountAmount() < 0 || $order->getBaseDiscountAmount() > 0) {
389
+ if ($result['discount_description'] == '') {
390
+ $result['discount_description'] = $order->getCouponCode();
391
+ }
392
+ $result['footer_html'] .= <<<EOT
393
+ <div class="grid discounts_grid">
394
+ <table><thead>
395
+ <tr class="headings">
396
+ <th>{$adminHelper->__('Discount')}: {$result['discount_description']}</th>
397
+ <th class="a-right"><strong>{$result['discount_amount']}</strong></th>
398
+ </tr>
399
+ </thead>
400
+ <tbody>
401
+ </tbody>
402
+ </table>
403
+ </div>
404
+
405
+ EOT;
406
+ }
407
+ $date = $order->getCreatedAtFormated('medium');
408
+ $result['title_html'] = htmlspecialchars(sprintf(Mage::helper('sales')->__('Order #%s - %s'), $order->getIncrementId(), $date));
409
+ $result['products'] = $products;
410
+ }
411
+
412
+ }
413
+ return $result;
414
+ }
415
+
416
+ /**
417
+ * Returns JS code in the footer for this concrete action.
418
+ *
419
+ * @param string $currentJs previusly generated JS
420
+ * @return string
421
+ */
422
+ protected function _getAdditionalJs($currentJs) {
423
+ $js = '';
424
+ if (!Mage::getStoreConfig('eabi_livehandler/admintools/enabled')) {
425
+ return $js;
426
+ }
427
+ /* @var $adminHelper Mage_Adminhtml_Helper_Data */
428
+ $adminHelper = Mage::helper('adminhtml');
429
+ $showProductsText = Mage::helper('eabi_livehandler')->__('Show order info');
430
+ $showProductsText = htmlspecialchars(str_replace('\\\'', '\'', addslashes($showProductsText)));
431
+
432
+ $additionalCssClass = '';
433
+ if (substr(Mage::getVersion(), 0, 3) == '1.3') {
434
+ $additionalCssClass = ' eabi_window_bg';
435
+ }
436
+ $longOnClicks = array();
437
+
438
+ foreach (self::$_allowedActions as $allowedAction) {
439
+ $actionButtonModel = Mage::getSingleton('eabi_livehandler/action_' . $allowedAction);
440
+ /* @var $actionButtonModel Eabi_Livehandler_Model_Action_Abstract */
441
+ if ($actionButtonModel && $actionButtonModel instanceof Eabi_Livehandler_Model_Action_Abstract) {
442
+ if ($actionButtonModel->getLongOnClick() && strlen($actionButtonModel->getLongOnClick())) {
443
+ $longOnClicks[$actionButtonModel->getCode()] = $actionButtonModel->getLongOnClick();
444
+ }
445
+ }
446
+ }
447
+
448
+
449
+
450
+
451
+ $js .= <<<EOT
452
+
453
+ var clickFirst = false, clickLast = false;
454
+ var eabiAdmintoolsGrid = function() {
455
+
456
+ var position = 0,
457
+ filters = $$('tr.filter').first().childElements(),
458
+ needs_reload = false,
459
+ pageNum = parseInt($$('input[name=page]').first().getValue(), 10),
460
+ pageLimit = parseInt($$('select[name=limit]').first().getValue(), 10),
461
+ current_products = [];
462
+
463
+ if (Control.Modal.current) {
464
+ Control.Modal.current.close();
465
+ }
466
+
467
+ for (var i = 0, cnt = filters.length; i < cnt; i++) {
468
+ if (filters[i].select('input[name=billing_name]').length == 1) {
469
+ position = i;
470
+ break;
471
+ }
472
+ }
473
+
474
+
475
+ var rows = $$('table#sales_order_grid_table tbody tr');
476
+ if (rows.length == 1) {
477
+ return;
478
+ }
479
+
480
+ for (var i = 0, cnt = rows.length; i < cnt; i++) {
481
+ rows[i].childElements()[position].insert('<button id="eabi-order_' + rows[i].select('input[class=massaction-checkbox]').first().readAttribute('value') + '" style="float: right;" class="scalable show-hide eabi-order-products" onclick="Event.stop(event); return false;" type="button"><span><b>{$showProductsText}</b></span></button>', { position: 'bottom'})
482
+ }
483
+
484
+ var style_window = function(container, options) {
485
+ var window_header = new Element('div', { className: 'window_header'});
486
+ var window_title = new Element('div', { className: 'window_title'});
487
+ var window_close = new Element('div', { className: 'window_close'});
488
+ var window_contents = new Element('div', { className: 'window_contents'});
489
+
490
+ var w = new Control.Modal(container, Object.extend({
491
+ className: 'eabi_window{$additionalCssClass}',
492
+ /*closeOnClick: window_close,*/
493
+ draggable: window_header,
494
+ insertRemoteContentAt: window_contents,
495
+ iframeshim: false,
496
+ afterOpen: function() {
497
+ var eabi_order_products = $$('button.eabi-order-products').toArray(),
498
+ order_id = options.caller.readAttribute('id').split('_')[1],
499
+ post_params = {'order_id': order_id };
500
+ if (options.target) {
501
+ post_params.action = options.target;
502
+ }
503
+ if (options.extra_data) {
504
+ post_params.extra_data = Object.toJSON(options.extra_data);
505
+ }
506
+ // window_title.update(container.readAttribute('title'));
507
+ if (options.caller) {
508
+ window_close.observe('click', function(event) {
509
+ if (Control.Modal.current) {
510
+ Control.Modal.current.close();
511
+ }
512
+ if (needs_reload) {
513
+ window.location.reload();
514
+ }
515
+ });
516
+ new Ajax.Request(action_url, {
517
+ method: 'post',
518
+ parameters: post_params,
519
+ evalJSON: 'force',
520
+ onSuccess: function(transport) {
521
+ var html = '';
522
+ if (transport.responseJSON) {
523
+ var html = '', json = transport.responseJSON, longClicks = {};
524
+ if (json.set_location) {
525
+ setLocation(json.set_location);
526
+ return;
527
+ }
528
+ if (json.success) {
529
+ html += json.customer_address_html;
530
+ html += json.header_html;
531
+ json.products.each(function(item) {
532
+ html += item.html;
533
+ });
534
+ html += json.footer_html;
535
+ window_contents.update(html);
536
+ window_title.update(json.title_html);
537
+ //add the click handlers....
538
+
539
+
540
+ EOT;
541
+ foreach ($longOnClicks as $key => $longOnClick) {
542
+ $js .= <<<EOT
543
+ longClicks[{$this->_encode($key)}] = function(event, caller, json) {
544
+ {$longOnClick};
545
+ };
546
+
547
+ EOT;
548
+ }
549
+
550
+
551
+
552
+
553
+ $js .= <<<EOT
554
+
555
+ window_contents.insert({before: '<span id="eabi_previous" class="eabi_arrow" title="' + {$this->_encode($adminHelper->__('Previous'))} + '" onclick="Podium.keydown(38);"></span><span id="eabi_next" class="eabi_arrow" title="' + {$this->_encode($adminHelper->__('Next'))}+ '" onclick="Podium.keydown(40);"></span>'});
556
+
557
+ window_contents.select('.eabi_orderview_actionbutton').each(function(item) {
558
+ var item_id = item.readAttribute('id'), passed = true;
559
+ if (item_id) {
560
+ item.observe('click', function(event) {
561
+ EOT;
562
+ //here goes extra long click
563
+ /*
564
+ * var longClicks = {
565
+ * 'comment' : function(event) { function body goes here... },
566
+ * 'cancel' : function(event) { function body goes here... },
567
+ * };
568
+ *
569
+ * if (longClicks[item_id.replace('eabi_', '')]) {
570
+ * longClicks[item_id.replace('eabi_', '')](event);
571
+ * }
572
+ *
573
+ */
574
+
575
+ $js .= <<<EOT
576
+
577
+ if (longClicks[item_id.replace('eabi_', '')]) {
578
+ passed = longClicks[item_id.replace('eabi_', '')](event, options.caller, json);
579
+ }
580
+ if (passed !== false) {
581
+ handleButtonClick(item_id.replace('eabi_', ''), options.caller, passed);
582
+ }
583
+ event.stop();
584
+ });
585
+ }
586
+ });
587
+ if (json.needs_reload) {
588
+ needs_reload = true;
589
+ }
590
+
591
+ } else {
592
+ window.location.reload();
593
+ }
594
+ } else {
595
+ window.location.reload();
596
+ }
597
+ }
598
+ });
599
+ }
600
+ if (options.hasOwnProperty('callerIndex')) {
601
+ document.observe('keydown', function(event) {
602
+ var keyCode = event.keyCode;
603
+ if (keyCode == 38 || keyCode == 37) {
604
+ if (options.callerIndex > 0) {
605
+ eabi_order_products[options.callerIndex - 1].click();
606
+ } else {
607
+ if (eabi_order_products.length && pageNum > 1) {
608
+ sales_order_gridJsObject.setPage(pageNum - 1, false);
609
+ clickLast = true;
610
+ }
611
+ }
612
+ return event.stop();
613
+ } else if (keyCode == 40 || keyCode == 39) {
614
+ if (options.callerIndex < (eabi_order_products.length - 1)) {
615
+ eabi_order_products[options.callerIndex + 1].click();
616
+ } else {
617
+ if (eabi_order_products && eabi_order_products.length >= pageLimit) {
618
+ sales_order_gridJsObject.setPage(pageNum + 1, true);
619
+ clickFirst = true;
620
+ }
621
+ }
622
+ return event.stop();
623
+ } else if (keyCode == 27) {
624
+ //esc key
625
+ Control.Modal.current.close(event);
626
+ if (needs_reload) {
627
+ window.location.reload();
628
+ }
629
+ return event.stop();
630
+ }
631
+ return event;
632
+ });
633
+ }
634
+ },
635
+ afterClose: function() {
636
+ if (options.caller) {
637
+ options.caller.removeClassName('disabled');
638
+ }
639
+ if (options.hasOwnProperty('callerIndex')) {
640
+ document.stopObserving('keydown');
641
+ $$('eabi_orderview_actionbutton').each(function(item) {
642
+ item.stopObserving('click');
643
+ });
644
+ window_close.stopObserving('click');
645
+ }
646
+ this.destroy();
647
+ }
648
+ }, options || {}));
649
+
650
+ w.container.insert(window_header);
651
+ window_header.insert(window_title);
652
+ window_header.insert(window_close);
653
+ w.container.insert(window_contents);
654
+ return w;
655
+ };
656
+ $$('button.eabi-order-products').each(function(item, index) {
657
+ var order_id = item.readAttribute('id').split('_')[1];
658
+ item.observe('click', function(event) {
659
+ item.addClassName('disabled');
660
+ var event_arguments = { title: '', caller: item, callerIndex: index};
661
+ if (event.memo && event.memo.target) {
662
+ event_arguments.target = event.memo.target;
663
+ }
664
+ var style_window_one = style_window('', event_arguments);
665
+ style_window_one.open();
666
+ event.stop();
667
+ });
668
+ item.observe('eabi:click', function(event) {
669
+ item.addClassName('disabled');
670
+ var event_arguments = { title: '', caller: item, callerIndex: index};
671
+ if (event.memo && event.memo.target) {
672
+ event_arguments.target = event.memo.target;
673
+ }
674
+ if (event.memo && event.memo.extra_data) {
675
+ event_arguments.extra_data = event.memo.extra_data;
676
+ }
677
+ var style_window_one = style_window('', event_arguments);
678
+ style_window_one.open();
679
+ event.stop();
680
+ });
681
+ });
682
+ if (clickFirst) {
683
+ current_products = $$('button.eabi-order-products').toArray();
684
+ if (current_products && current_products[0]) {
685
+ current_products[0].click();
686
+ }
687
+ clickFirst = false;
688
+ }
689
+ if (clickLast) {
690
+ current_products = $$('button.eabi-order-products').toArray();
691
+ if (current_products && current_products.length) {
692
+ current_products[current_products.length - 1].click();
693
+ }
694
+ clickLast = false;
695
+ }
696
+
697
+
698
+ }; //end of eabiAdminToolsGrid
699
+
700
+ var oldInitGridRows = false;
701
+ if (Control && Control.Modal) {
702
+ eabiAdmintoolsGrid();
703
+ oldInitGridRows = sales_order_gridJsObject.initGridRows;
704
+ }
705
+
706
+ if (oldInitGridRows) {
707
+ sales_order_gridJsObject.initGridRows = function() {
708
+ $$('button.eabi-order-products').each(function(item) {
709
+ item.stopObserving('click');
710
+ item.stopObserving('eabi:click');
711
+ });
712
+ oldInitGridRows();
713
+ eabiAdmintoolsGrid();
714
+ };
715
+ } else if (Control && Control.Modal) {
716
+ var oldInitGridRows = varienGrid.prototype.reload;
717
+ varienGrid.prototype.reload = function(url) {
718
+ $$('button.eabi-order-products').each(function(item) {
719
+ item.stopObserving('click');
720
+ item.stopObserving('eabi:click');
721
+ });
722
+
723
+ if (!this.reloadParams) {
724
+ this.reloadParams = {form_key: FORM_KEY};
725
+ }
726
+ else {
727
+ this.reloadParams.form_key = FORM_KEY;
728
+ }
729
+ url = url || this.url;
730
+ if(this.useAjax){
731
+ new Ajax.Request(url + ((url.match(new RegExp('\\\\?')) ? '&ajax=true' : '?ajax=true') ), {
732
+ loaderArea: this.containerId,
733
+ parameters: this.reloadParams || {},
734
+ evalScripts: true,
735
+ onFailure: this._processFailure.bind(this),
736
+ onComplete: this.initGrid.bind(this),
737
+ onSuccess: function(transport) {
738
+ try {
739
+ if (transport.responseText.isJSON()) {
740
+ var response = transport.responseText.evalJSON()
741
+ if (response.error) {
742
+ alert(response.message);
743
+ }
744
+ if(response.ajaxExpired && response.ajaxRedirect) {
745
+ setLocation(response.ajaxRedirect);
746
+ }
747
+ } else {
748
+ $(this.containerId).update(transport.responseText);
749
+ eabiAdmintoolsGrid();
750
+ }
751
+ }
752
+ catch (e) {
753
+ $(this.containerId).update(transport.responseText);
754
+ }
755
+ }.bind(this)
756
+ });
757
+ return;
758
+ }
759
+ else{
760
+ if(this.reloadParams){
761
+ \$H(this.reloadParams).each(function(pair){
762
+ url = this.addVarToUrl(pair.key, pair.value);
763
+ }.bind(this));
764
+ }
765
+ location.href = url;
766
+ }
767
+
768
+
769
+ };
770
+
771
+ }
772
+
773
+ //handle the clicks......
774
+ function handleButtonClick(call_action, caller_button, passed) {
775
+ //make ajax request
776
+ caller_button.fire('eabi:click', { target: call_action, extra_data: passed});
777
+ }
778
+
779
+
780
+
781
+
782
+ EOT;
783
+
784
+
785
+ return $js;
786
+ }
787
+
788
+ private function __makeActionButton($title, $onclick = '', $cssClass = '', $id = '') {
789
+ $html = '';
790
+
791
+ $onclick = htmlspecialchars(str_replace('\\\'', '\'', addslashes($onclick)));
792
+
793
+ $html .= <<<EOT
794
+ <button class="scalable eabi_orderview_actionbutton {$cssClass}" id="{$id}" type="button" onclick="{$onclick}"><span>{$title}</span></button>
795
+
796
+ EOT;
797
+ return $html;
798
+ }
799
+
800
+ protected function _encode($input) {
801
+ return json_encode($input);
802
+
803
+ }
804
+
805
+ /**
806
+ *
807
+ * @return Eabi_Livehandler_Helper_Data
808
+ */
809
+ protected function getEabi() {
810
+ return Mage::helper('eabi');
811
+ }
812
+
813
+ }
814
+
app/code/community/Eabi/Livehandler/Model/System/Config/Backend/Button.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * Description of Button
30
+ *
31
+ * @author Matis
32
+ */
33
+ class Eabi_Livehandler_Model_System_Config_Backend_Button extends Mage_Adminhtml_Model_System_Config_Backend_Serialized_Array {
34
+ protected $_eventPrefix = 'eabi_livehandler_system_config_backend_button';
35
+
36
+ protected function _afterLoad() {
37
+ parent::_afterLoad();
38
+ // Zend_Debug::dump($this->getValue());
39
+ $dirnames = array(
40
+ Mage::getBaseDir('code').'/community/Eabi/Livehandler/Model/Action',
41
+ Mage::getBaseDir('code').'/local/Eabi/Livehandler/Model/Action',
42
+ );
43
+ $foundNames = array();
44
+ foreach ($dirnames as $dirname) {
45
+ if (is_dir($dirname)) {
46
+ $directoryLister = new Varien_Directory_Collection($dirname, true);
47
+ $filenames = $directoryLister->filesPaths();
48
+ foreach ($filenames as $filename) {
49
+ $className = str_replace(array($dirname, '\\'), array('', '/'), $filename);
50
+ if (strpos($className, '.php') !== false) {
51
+ $className = ltrim(strtolower(str_replace(array('.php', '/'), array('', '_'), $className)), '_');
52
+ if ($className != 'abstract') {
53
+ $foundNames[$className] = $className;
54
+ }
55
+ }
56
+ }
57
+
58
+ }
59
+ }
60
+ $values = $this->getValue();
61
+ $existingValues = array();
62
+ $valuesToRemove = array();
63
+ if (is_array($values)) {
64
+ uasort($values, array(__CLASS__, '_sortAction'));
65
+
66
+ foreach ($values as $key => $value) {
67
+ $existingValues[$value['button_name']] = $value['button_name'];
68
+ $testModel = Mage::getModel('eabi_livehandler/action_'.$value['button_name']);
69
+ if (!$testModel || !($testModel instanceof Eabi_Livehandler_Model_Action_Abstract)) {
70
+ $valuesToRemove[] = $key;
71
+ }
72
+
73
+ }
74
+ }
75
+ if (count($foundNames) && !is_array($values)) {
76
+ $values = array();
77
+ }
78
+ $valueAdded = false;
79
+ foreach ($foundNames as $foundName) {
80
+ if (!isset($existingValues[$foundName])) {
81
+ $testModel = Mage::getModel('eabi_livehandler/action_'.$foundName);
82
+ if ($testModel && $testModel instanceof Eabi_Livehandler_Model_Action_Abstract) {
83
+ $values['_' . time() . '_' . mt_rand(0, 999)] = array(
84
+ 'button_name' => $foundName,
85
+ 'sort_order' => "0",
86
+ 'disabled' => "0"
87
+ );
88
+ $valueAdded = true;
89
+ }
90
+
91
+ }
92
+ }
93
+ foreach ($valuesToRemove as $valueToRemove) {
94
+ unset($values[$valueToRemove]);
95
+ }
96
+ if ($valueAdded || count($valuesToRemove) || true) {
97
+ $this->setValue($values);
98
+ }
99
+
100
+
101
+ }
102
+
103
+ public function isValueChanged() {
104
+ $oldValue = @unserialize($this->getOldValue());
105
+ if (is_array($oldValue)) {
106
+ return $oldValue != $this->getValue();
107
+ }
108
+ return parent::isValueChanged();
109
+ }
110
+
111
+
112
+ public static function _sortAction($a, $b) {
113
+ if ((bool)$a['disabled'] && !(bool)$b['disabled']) {
114
+ return 1;
115
+ }
116
+ if (!(bool)$a['disabled'] && (bool)$b['disabled']) {
117
+ return -1;
118
+ }
119
+ if ((int)$a['sort_order'] == (int)$b['sort_order']) {
120
+ return 0;
121
+ }
122
+ return (int)$a['sort_order'] < (int)$b['sort_order']?-1:1;
123
+ }
124
+
125
+
126
+ }
127
+
app/code/community/Eabi/Livehandler/controllers/Adminhtml/LivehandlerController.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * <p>Handles the AJAX request defined for the backend (from database table eabi_livehandler)</p>
30
+ * <p>Security model: If Page itself can be displayed, then it is allowed to run actions, which are bound to current Livehandler model. If user switches page, then current model actions cannot be run any more.</p>
31
+ * <p>If user does not show any activity for Eabi_Livehandler_IndexController::ALLOWED time seconds, then actions cannot be run after timeout.</p>
32
+ *
33
+ * @author matishalmann
34
+ */
35
+ class Eabi_Livehandler_Adminhtml_LivehandlerController extends Mage_Adminhtml_Controller_Action {
36
+ const ALLOWED_TIME = 1800;
37
+ protected function _initAction() {
38
+ return $this;
39
+ }
40
+
41
+ public function processAction() {
42
+ $result = array();
43
+ if (!$this->_getEabi()->getConfigData('eabi_livehandler/main/enabled')) {
44
+ throw new Exception('Module Eabi Livehandler is not enabled');
45
+ }
46
+ /*
47
+ * Check if the process is in Session allowed list.
48
+ *
49
+ */
50
+ $session = Mage::getSingleton('core/session');
51
+ $time = time();
52
+
53
+ $processName = base64_decode($this->getRequest()->getParam('__path'));
54
+ $processEntries = $session->getData('eabi_livehandler_entries');
55
+ $website = Mage::app()->getStore()->getWebsiteId();
56
+ $store = Mage::app()->getStore()->getStoreId();
57
+
58
+
59
+ if (is_array($processEntries) && isset($processEntries[$processName]) && $time - $processEntries[$processName] < self::ALLOWED_TIME) {
60
+
61
+
62
+ //execute the action.
63
+ $isAdmin = true;
64
+ $model = $processName;
65
+
66
+ //get action by action name, website, store, is_admin = false
67
+ $actionsCollection = Mage::getModel('eabi_livehandler/entry')->getCollection()->setModelFilter($model, $isAdmin, $website, $store);
68
+
69
+ $classesRan = array();
70
+
71
+ $result = array();
72
+ foreach ($actionsCollection as $action) {
73
+ $action->load($action->getId());
74
+ if (isset($classesRan[$action->getModelClass()])) {
75
+ continue;
76
+ }
77
+
78
+
79
+ if (!isset($result['_is_error']) || !$result['_is_error']) {
80
+ $processEntries[$processName] = $time;
81
+ $result = $action->runAdmin($this->getRequest()->getPost());
82
+ }
83
+ $classesRan[$action->getModelClass()] = true;
84
+
85
+ }
86
+
87
+
88
+
89
+ Mage::getSingleton('core/session')->setData('eabi_livehandler_entries', $processEntries);
90
+ } else {
91
+ $result['_is_error'] = true;
92
+ }
93
+
94
+
95
+ echo Zend_Json::encode($result);
96
+ die();
97
+ }
98
+ /**
99
+ *
100
+ * @return Eabi_Livehandler_Helper_Data
101
+ */
102
+ protected function _getEabi() {
103
+ return Mage::helper('eabi');
104
+ }
105
+
106
+
107
+ }
108
+
109
+
app/code/community/Eabi/Livehandler/controllers/Adminhtml/RemoveController.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * <p>Removes old livehandler from app/local folder</p>
30
+ *
31
+ * @author matishalmann
32
+ */
33
+ class Eabi_Livehandler_Adminhtml_RemoveController extends Mage_Adminhtml_Controller_Action {
34
+ protected function _initAction() {
35
+ return $this;
36
+ }
37
+
38
+ public function removeAction() {
39
+ $result = array('status' => 'failed');
40
+ if ($this->getRequest()->isPost() && $this->getRequest()->getPost('remove') == 'true') {
41
+ $dirName = Mage::getBaseDir('code').'/local/Eabi/Livehandler';
42
+ if (is_dir($dirName) && file_exists($dirName.'/etc/config.xml')) {
43
+ $directory = new Varien_Io_File();
44
+ $deleteResult = $directory->rmdir($dirName, true);
45
+ if ($deleteResult) {
46
+ $result['status'] = 'success';
47
+ }
48
+ }
49
+
50
+ }
51
+ $this->getResponse()->setRawHeader('Content-type: application/json');
52
+ $this->getResponse()->setBody(json_encode($result));
53
+ return;
54
+ }
55
+
56
+ /**
57
+ *
58
+ * @return Eabi_Livehandler_Helper_Data
59
+ */
60
+ protected function _getEabi() {
61
+ return Mage::helper('eabi');
62
+ }
63
+
64
+
65
+ }
66
+
67
+
app/code/community/Eabi/Livehandler/controllers/IndexController.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ /**
29
+ * <p>Handles the AJAX request defined for the frontend (from database table eabi_livehandler)</p>
30
+ * <p>Security model: If Page itself can be displayed, then it is allowed to run actions, which are bound to current Livehandler model. If user switches page, then current model actions cannot be run any more.</p>
31
+ * <p>If user does not show any activity for Eabi_Livehandler_IndexController::ALLOWED time seconds, then actions cannot be run after timeout.</p>
32
+ *
33
+ *
34
+ * @author matishalmann
35
+ */
36
+ class Eabi_Livehandler_IndexController extends Mage_Core_Controller_Front_Action {
37
+ const ALLOWED_TIME = 1800;
38
+ public function processAction() {
39
+ $result = array();
40
+ if (!$this->_getEabi()->getConfigData('eabi_livehandler/main/enabled')) {
41
+ throw new Exception('Module Eabi Livehandler is not enabled');
42
+ }
43
+ /*
44
+ * Check if the process is in Session allowed list.
45
+ *
46
+ */
47
+ $session = Mage::getSingleton('core/session');
48
+ $time = time();
49
+
50
+ $processName = base64_decode($this->getRequest()->getParam('__path'));
51
+ // $processName = $this->getRequest()->getParam('processName');
52
+ $processEntries = $session->getData('eabi_livehandler_entries');
53
+ $website = Mage::app()->getStore()->getWebsiteId();
54
+ $store = Mage::app()->getStore()->getStoreId();
55
+
56
+
57
+ if (is_array($processEntries) && isset($processEntries[$processName]) && $time - $processEntries[$processName] < self::ALLOWED_TIME) {
58
+
59
+
60
+ //execute the action.
61
+ $isAdmin = false;
62
+ $model = $processName;
63
+
64
+ //get action by action name, website, store, is_admin = false
65
+ $actionsCollection = Mage::getModel('eabi_livehandler/entry')->getCollection()->setModelFilter($model, $isAdmin, $website, $store);
66
+
67
+ $result = array();
68
+ $classesRan = array();
69
+ foreach ($actionsCollection as $action) {
70
+ $action->load($action->getId());
71
+ if (isset($classesRan[$action->getModelClass()])) {
72
+ continue;
73
+ }
74
+
75
+ if (!isset($result['_is_error']) || !$result['_is_error']) {
76
+ $processEntries[$processName] = $time;
77
+ $action->runPublic($this->getRequest()->getPost());
78
+ }
79
+ $classesRan[$action->getModelClass()] = true;
80
+
81
+ }
82
+
83
+
84
+
85
+ Mage::getSingleton('core/session')->setData('eabi_livehandler_entries', $processEntries);
86
+ }
87
+
88
+
89
+ echo Zend_Json::encode($result);
90
+ die();
91
+ }
92
+
93
+ /**
94
+ *
95
+ * @return Eabi_Livehandler_Helper_Data
96
+ */
97
+ protected function _getEabi() {
98
+ return Mage::helper('eabi');
99
+ }
100
+
101
+ }
102
+
app/code/community/Eabi/Livehandler/etc/config.xml ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ /**
4
+ @category Eabi
5
+ @package Eabi_Livehandler
6
+ @author Aktsiamaailm OU
7
+ @licence http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+
9
+ */
10
+ -->
11
+
12
+
13
+ <config>
14
+ <modules>
15
+ <Eabi_Livehandler>
16
+ <version>0.1.1</version>
17
+ </Eabi_Livehandler>
18
+ </modules>
19
+
20
+ <admin>
21
+ <routers>
22
+ <eabi_livehandler>
23
+ <use>admin</use>
24
+ <args>
25
+ <module>Eabi_Livehandler</module>
26
+ <frontName>eabi_livehandler</frontName>
27
+ </args>
28
+ </eabi_livehandler>
29
+ </routers>
30
+ </admin>
31
+
32
+ <frontend>
33
+ <routers>
34
+ <eabi_livehandler>
35
+ <use>standard</use>
36
+ <args>
37
+ <module>Eabi_Livehandler</module>
38
+ <frontName>eabi_livehandler</frontName>
39
+ </args>
40
+ </eabi_livehandler>
41
+ </routers>
42
+ <layout>
43
+ <updates>
44
+ <eabi_livehandler>
45
+ <file>eabi_livehandler.xml</file>
46
+ </eabi_livehandler>
47
+ </updates>
48
+ </layout>
49
+ </frontend>
50
+
51
+ <adminhtml>
52
+ <translate>
53
+ <modules>
54
+ <eabi_livehandler>
55
+ <files>
56
+ <default>Eabi_Livehandler.csv</default>
57
+ </files>
58
+ </eabi_livehandler>
59
+ </modules>
60
+ </translate>
61
+ <acl>
62
+ <resources>
63
+ <all>
64
+ <title>Allow Everything</title>
65
+ </all>
66
+ <admin>
67
+ <children>
68
+ <system>
69
+ <children>
70
+ <config>
71
+ <children>
72
+ <eabi_livehandler>
73
+ <title>Live Handler from Eabi.ee</title>
74
+ </eabi_livehandler>
75
+ </children>
76
+ </config>
77
+ </children>
78
+ </system>
79
+ </children>
80
+ </admin>
81
+ </resources>
82
+ </acl>
83
+ <layout>
84
+ <updates>
85
+ <eabi_livehandler>
86
+ <file>eabi_livehandler.xml</file>
87
+ </eabi_livehandler>
88
+ </updates>
89
+ </layout>
90
+
91
+ </adminhtml>
92
+
93
+ <global>
94
+ <models>
95
+ <eabi_livehandler>
96
+ <class>Eabi_Livehandler_Model</class>
97
+ <resourceModel>eabi_livehandler_mysql4</resourceModel>
98
+ </eabi_livehandler>
99
+ <eabi_livehandler_mysql4>
100
+ <class>Eabi_Livehandler_Model_Mysql4</class>
101
+ <entities>
102
+ <entry>
103
+ <table>eabi_livehandler</table>
104
+ </entry>
105
+ </entities>
106
+ </eabi_livehandler_mysql4>
107
+ </models>
108
+ <blocks>
109
+ <eabi_livehandler>
110
+ <class>Eabi_Livehandler_Block</class>
111
+ </eabi_livehandler>
112
+ </blocks>
113
+ <resources>
114
+ <eabi_livehandler_setup>
115
+ <setup>
116
+ <module>Eabi_Livehandler</module>
117
+ </setup>
118
+ <connection>
119
+ <use>core_setup</use>
120
+ </connection>
121
+ </eabi_livehandler_setup>
122
+ <eabi_livehandler_write>
123
+ <setup>
124
+ <module>Eabi_Livehandler</module>
125
+ </setup>
126
+ <connection>
127
+ <use>core_write</use>
128
+ </connection>
129
+ </eabi_livehandler_write>
130
+ <eabi_livehandler_read>
131
+ <setup>
132
+ <module>Eabi_Livehandler</module>
133
+ </setup>
134
+ <connection>
135
+ <use>core_read</use>
136
+ </connection>
137
+ </eabi_livehandler_read>
138
+ </resources>
139
+ <helpers>
140
+ <eabi>
141
+ <class>Eabi_Livehandler_Helper</class>
142
+ </eabi>
143
+ <eabi_livehandler>
144
+ <class>Eabi_Livehandler_Helper</class>
145
+ </eabi_livehandler>
146
+ </helpers>
147
+ <translate>
148
+ <modules>
149
+ <eabi_livehandler>
150
+ <files>
151
+ <default>Eabi_Livehandler.csv</default>
152
+ </files>
153
+ </eabi_livehandler>
154
+ </modules>
155
+ </translate>
156
+ </global>
157
+ <default>
158
+ <eabi_livehandler>
159
+ <main>
160
+ <enabled>1</enabled>
161
+ </main>
162
+ <admintools>
163
+ <enabled>1</enabled>
164
+ <open_in_new>1</open_in_new>
165
+ <buttons_url>http://en.e-abi.ee/magento/better-order-management.html</buttons_url>
166
+ </admintools>
167
+ </eabi_livehandler>
168
+ </default>
169
+ </config>
app/code/community/Eabi/Livehandler/etc/system.xml ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ /**
4
+ @category Eabi
5
+ @package Eabi_Livehandler
6
+ @author Aktsiamaailm OU
7
+ @licence http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+
9
+ */
10
+ -->
11
+
12
+
13
+ <config>
14
+ <tabs>
15
+ <eabi translate="label" module="eabi_livehandler">
16
+ <label>Eabi Moodulid</label>
17
+ <sort_order>300</sort_order>
18
+ </eabi>
19
+ </tabs>
20
+ <sections>
21
+ <eabi_livehandler translate="label" module="eabi_livehandler">
22
+ <label>Eabi Livehandler</label>
23
+ <tab>eabi</tab>
24
+ <sort_order>340</sort_order>
25
+ <show_in_default>1</show_in_default>
26
+ <show_in_website>1</show_in_website>
27
+ <show_in_store>1</show_in_store>
28
+ <groups>
29
+ <main translate="label" module="eabi_livehandler">
30
+ <label>Global Livehandler settings</label>
31
+ <tab>eabi</tab>
32
+ <sort_order>0</sort_order>
33
+ <show_in_default>1</show_in_default>
34
+ <show_in_website>1</show_in_website>
35
+ <show_in_store>1</show_in_store>
36
+ <fields>
37
+ <enabled translate="label">
38
+ <label>Enabled</label>
39
+ <frontend_type>select</frontend_type>
40
+ <source_model>adminhtml/system_config_source_yesno</source_model>
41
+ <sort_order>10</sort_order>
42
+ <show_in_default>1</show_in_default>
43
+ <show_in_website>0</show_in_website>
44
+ <show_in_store>0</show_in_store>
45
+ </enabled>
46
+ </fields>
47
+ </main>
48
+ <admintools translate="label" module="eabi_livehandler">
49
+ <label>Admin Order Grid Helper</label>
50
+ <tab>eabi</tab>
51
+ <sort_order>340</sort_order>
52
+ <show_in_default>1</show_in_default>
53
+ <show_in_website>1</show_in_website>
54
+ <show_in_store>1</show_in_store>
55
+ <comment><![CDATA[
56
+ <a href="http://www.e-abi.ee" target="_blank"><IMG border="0" src="http://www.e-abi.ee/skin/frontend/default/electronics3/images/logo-web.png"></a>
57
+ <div style="margin-top:4px;margin-bottom:4px; color:gray;"></div>
58
+ <div style="margin-top:4px;"><b>Aktsiamaailm OÜ, <a href="mailto:info@e-abi.ee">info@e-abi.ee</a></b></div>
59
+ <hr /><br />
60
+
61
+ ]]>
62
+ </comment>
63
+ <fields>
64
+ <enabled translate="label">
65
+ <label>Enabled</label>
66
+ <frontend_type>select</frontend_type>
67
+ <source_model>adminhtml/system_config_source_yesno</source_model>
68
+ <sort_order>1</sort_order>
69
+ <show_in_default>1</show_in_default>
70
+ <show_in_website>0</show_in_website>
71
+ <show_in_store>0</show_in_store>
72
+ </enabled>
73
+ <open_in_new translate="label">
74
+ <label>Open Order in new window when clicking on Order status</label>
75
+ <frontend_type>select</frontend_type>
76
+ <source_model>adminhtml/system_config_source_yesno</source_model>
77
+ <sort_order>2</sort_order>
78
+ <show_in_default>1</show_in_default>
79
+ <show_in_website>0</show_in_website>
80
+ <show_in_store>0</show_in_store>
81
+ </open_in_new>
82
+ <buttons translate="">
83
+ <label></label>
84
+ <frontend_model>eabi_livehandler/adminhtml_config_form_field_button</frontend_model>
85
+ <backend_model>eabi_livehandler/system_config_backend_button</backend_model>
86
+ <sort_order>3</sort_order>
87
+ <show_in_default>1</show_in_default>
88
+ <show_in_website>0</show_in_website>
89
+ <show_in_store>0</show_in_store>
90
+ </buttons>
91
+ <removal translate="">
92
+ <label></label>
93
+ <frontend_model>eabi_livehandler/adminhtml_config_form_field_remove</frontend_model>
94
+ <sort_order>4</sort_order>
95
+ <show_in_default>1</show_in_default>
96
+ <show_in_website>0</show_in_website>
97
+ <show_in_store>0</show_in_store>
98
+ </removal>
99
+ </fields>
100
+
101
+ </admintools>
102
+
103
+ </groups>
104
+ </eabi_livehandler>
105
+ </sections>
106
+
107
+ </config>
app/code/community/Eabi/Livehandler/sql/eabi_livehandler_setup/mysql4-install-0.1.0.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ $installer = $this;
29
+
30
+ $installer->startSetup();
31
+
32
+ $installer->run("
33
+ DROP TABLE IF EXISTS {$this->getTable('eabi_livehandler')};
34
+
35
+ CREATE TABLE {$this->getTable('eabi_livehandler')} (
36
+ `id` int(11) unsigned NOT NULL auto_increment,
37
+ `name` varchar(255) NOT NULL,
38
+ `is_enabled` tinyint(1) unsigned NOT NULL default 0,
39
+ `is_admin` tinyint(1) unsigned NOT NULL default 0,
40
+ `request_var` varchar(255) NOT NULL,
41
+ `store_id` int(11) unsigned NOT NULL default 0,
42
+ `website_id` int(11) unsigned NOT NULL default 0,
43
+ `model_class` varchar(255) NULL,
44
+ `created_time` datetime NULL,
45
+ `update_time` datetime NULL,
46
+ `cached_attributes` text NULL,
47
+ `parameters` text NULL,
48
+ `css` text NULL,
49
+ `js` text NULL,
50
+ `html` text NULL,
51
+
52
+ PRIMARY KEY (`id`)
53
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
54
+
55
+ ");
56
+
57
+
58
+ $installer->endSetup();
app/code/community/Eabi/Livehandler/sql/eabi_livehandler_setup/mysql4-upgrade-0.1.0-0.1.1.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * E-Abi (Aktsiamaailm LLC)
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to info@e-abi.ee so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade this module to newer
18
+ * versions in the future.
19
+ *
20
+ * @category Eabi
21
+ * @package Eabi_Livehandler
22
+ * @copyright Copyright (c) 2013 Aktsiamaailm LLC (http://en.e-abi.ee/)
23
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
24
+ * @author Matis Halmann <info@e-abi.ee>
25
+ *
26
+ */
27
+
28
+ $installer = $this;
29
+
30
+ $installer->startSetup();
31
+
32
+ $installer->run("
33
+
34
+ DELETE FROM {$this->getTable('eabi_livehandler')} WHERE model_class = 'eabi_admintools/ordergrid';
35
+
36
+ INSERT INTO {$this->getTable('eabi_livehandler')}
37
+ (name, is_enabled, is_admin, request_var, model_class, store_id, website_id, created_time, update_time)
38
+ VALUES
39
+ ('Adminhtml Sales Order Grid show products', 1, 1, 'adminhtml/sales_order/index', 'eabi_livehandler/ordergrid', 0, 0, NOW(), NOW());
40
+
41
+ INSERT INTO {$this->getTable('eabi_livehandler')}
42
+ (name, is_enabled, is_admin, request_var, model_class, store_id, website_id, created_time, update_time)
43
+ VALUES
44
+ ('Adminhtml Sales Order Grid show products', 1, 1, 'adminhtml/sales_order/view', 'eabi_livehandler/ordergrid', 0, 0, NOW(), NOW());
45
+
46
+
47
+ ");
48
+
49
+
50
+ $installer->endSetup();
app/design/adminhtml/default/default/layout/eabi_livehandler.xml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Käesoleva loomingu autoriõigused kuuluvad Matis Halmannile ja Aktsiamaailm OU-le
4
+ Litsentsitingimused on saadaval http://www.e-abi.ee/litsents
5
+ -->
6
+
7
+
8
+ <layout version="0.1.0">
9
+ <default>
10
+ <reference name="before_body_end">
11
+ <block type="eabi_livehandler/footer" name="eabi_livehandler_footer" as="eabi_livehandler_footer" />
12
+ </reference>
13
+ </default>
14
+ <adminhtml_sales_order_index>
15
+ <reference name="head">
16
+ <action method="addJs">
17
+ <script>livepipe/livepipe.js</script>
18
+ </action>
19
+ <action method="addJs">
20
+ <script>livepipe/window.js</script>
21
+ </action>
22
+ <action method="addJs">
23
+ <script>eabi_js/crossBrowser_initKeyboardEvent.js</script>
24
+ </action>
25
+ <action method="addCss">
26
+ <name>eabi_admintools.css</name>
27
+ </action>
28
+ </reference>
29
+ </adminhtml_sales_order_index>
30
+
31
+
32
+ </layout>
app/design/frontend/base/default/layout/eabi_livehandler.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Käesoleva loomingu autoriõigused kuuluvad Matis Halmannile ja Aktsiamaailm OU-le
4
+ Litsentsitingimused on saadaval http://www.e-abi.ee/litsents
5
+ -->
6
+
7
+
8
+ <layout version="0.1.0">
9
+ <default>
10
+ <reference name="before_body_end">
11
+ <block type="eabi_livehandler/footer" name="eabi_livehandler_footer" as="eabi_livehandler_footer" />
12
+ </reference>
13
+ </default>
14
+
15
+
16
+ </layout>
app/design/frontend/default/default/layout/eabi_livehandler.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Käesoleva loomingu autoriõigused kuuluvad Matis Halmannile ja Aktsiamaailm OU-le
4
+ Litsentsitingimused on saadaval http://www.e-abi.ee/litsents
5
+ -->
6
+
7
+
8
+ <layout version="0.1.0">
9
+ <default>
10
+ <reference name="before_body_end">
11
+ <block type="eabi_livehandler/footer" name="eabi_livehandler_footer" as="eabi_livehandler_footer" />
12
+ </reference>
13
+ </default>
14
+
15
+
16
+ </layout>
app/etc/modules/Eabi_Livehandler.xml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ /**
4
+ @category Eabi
5
+ @package Eabi_Livehandler
6
+ @author Aktsiamaailm OU
7
+ @licence http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+
9
+ */
10
+ -->
11
+
12
+
13
+ <config>
14
+ <modules>
15
+ <Eabi_Livehandler>
16
+ <active>true</active>
17
+ <codePool>community</codePool>
18
+ <version>0.1.1</version>
19
+ </Eabi_Livehandler>
20
+ </modules>
21
+ </config>
app/locale/en_US/Eabi_Livehandler.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Enabled","Enabled"
2
+ "Global Livehandler settings","Global Livehandler settings"
3
+ "Eabi Livehandler","Eabi Livehandler"
4
+ "Show order info","Show order info"
5
+ "Invalid action","Invalid action"
6
+ "Not applicable","Not applicable"
7
+ "Email Invoice","Email Invoice"
8
+ "Admin Order Grid Helper","Admin Order Grid Helper"
9
+ "Open Order in new window when clicking on Order status","Open Order in new window when clicking on Order status"
10
+ "Capture","Capture"
11
+ "Comment added!","Comment added!"
12
+ "Add button","Add button"
13
+ "Button name","Button name"
14
+ "Delete instance of this module from %s folder","Delete instance of this module from %s folder"
15
+ "Folder %s deleted!","Folder %s deleted!"
16
+ "Folder %s delete failed!","Folder %s delete failed!"
17
+ "Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?","Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?"
18
+ "Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>","Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>"
19
+ "http://en.e-abi.ee/magento/better-order-management.html","http://en.e-abi.ee/magento/better-order-management.html"
app/locale/et_EE/Eabi_Livehandler.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Enabled","Lubatud"
2
+ "Global Livehandler settings","Üleüldised seadistused"
3
+ "Eabi Livehandler","Eabi Livehandler"
4
+ "Show order info","Näita tellimust"
5
+ "Invalid action","Viga tellimuse töötlemisel"
6
+ "Not applicable","Pole saadaval"
7
+ "Email Invoice","Arve e-postile"
8
+ "Admin Order Grid Helper","Tellimuste nimekirja moodul"
9
+ "Open Order in new window when clicking on Order status","Ava tellimus uues aknas kui klikid Tellimuse oleku peale"
10
+ "Capture","Makstud!"
11
+ "Comment added!","Kommentaar lisatud!"
12
+ "Add button","Lisa nupp"
13
+ "Button name","Nupu nimetus"
14
+ "Delete instance of this module from %s folder","Kustuta käesoleva mooduli instants %s kaustast"
15
+ "Folder %s deleted!","Kaust %s kustutatud!"
16
+ "Folder %s delete failed!","Kausta %s ei õnnestunud kustutada!"
17
+ "Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?","Tõenäoliselt on vanem versioon käesolevast moodulist Teie süsteemis. Kas soovite selle kustutada %s kaustast?"
18
+ "Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>","Halda tellimust <a href='%s' target='_blank'>nupukeste abil</a> siitsamast!"
19
+ "http://en.e-abi.ee/magento/better-order-management.html","http://www.e-abi.ee/magento/better-order-management.html"
app/locale/fi_FI/Eabi_Livehandler.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Enabled","Enabled"
2
+ "Global Livehandler settings","Global Livehandler settings"
3
+ "Eabi Livehandler","Eabi Livehandler"
4
+ "Show order info","Show order info"
5
+ "Invalid action","Invalid action"
6
+ "Not applicable","Not applicable"
7
+ "Email Invoice","Email Invoice"
8
+ "Admin Order Grid Helper","Admin Order Grid Helper"
9
+ "Open Order in new window when clicking on Order status","Open Order in new window when clicking on Order status"
10
+ "Capture","Capture"
11
+ "Comment added!","Comment added!"
12
+ "Add button","Add button"
13
+ "Button name","Button name"
14
+ "Delete instance of this module from %s folder","Delete instance of this module from %s folder"
15
+ "Folder %s deleted!","Folder %s deleted!"
16
+ "Folder %s delete failed!","Folder %s delete failed!"
17
+ "Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?","Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?"
18
+ "Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>","Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>"
19
+ "http://en.e-abi.ee/magento/better-order-management.html","http://en.e-abi.ee/magento/better-order-management.html"
app/locale/lt_LT/Eabi_Livehandler.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Enabled","Enabled"
2
+ "Global Livehandler settings","Global Livehandler settings"
3
+ "Eabi Livehandler","Eabi Livehandler"
4
+ "Show order info","Show order info"
5
+ "Invalid action","Invalid action"
6
+ "Not applicable","Not applicable"
7
+ "Email Invoice","Email Invoice"
8
+ "Admin Order Grid Helper","Admin Order Grid Helper"
9
+ "Open Order in new window when clicking on Order status","Open Order in new window when clicking on Order status"
10
+ "Capture","Capture"
11
+ "Comment added!","Comment added!"
12
+ "Add button","Add button"
13
+ "Button name","Button name"
14
+ "Delete instance of this module from %s folder","Delete instance of this module from %s folder"
15
+ "Folder %s deleted!","Folder %s deleted!"
16
+ "Folder %s delete failed!","Folder %s delete failed!"
17
+ "Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?","Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?"
18
+ "Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>","Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>"
19
+ "http://en.e-abi.ee/magento/better-order-management.html","http://en.e-abi.ee/magento/better-order-management.html"
app/locale/sv_SE/Eabi_Livehandler.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Enabled","Enabled"
2
+ "Global Livehandler settings","Global Livehandler settings"
3
+ "Eabi Livehandler","Eabi Livehandler"
4
+ "Show order info","Show order info"
5
+ "Invalid action","Invalid action"
6
+ "Not applicable","Not applicable"
7
+ "Email Invoice","Email Invoice"
8
+ "Admin Order Grid Helper","Admin Order Grid Helper"
9
+ "Open Order in new window when clicking on Order status","Open Order in new window when clicking on Order status"
10
+ "Capture","Capture"
11
+ "Comment added!","Comment added!"
12
+ "Add button","Add button"
13
+ "Button name","Button name"
14
+ "Delete instance of this module from %s folder","Delete instance of this module from %s folder"
15
+ "Folder %s deleted!","Folder %s deleted!"
16
+ "Folder %s delete failed!","Folder %s delete failed!"
17
+ "Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?","Most probably you have older version of this module in the system. Do you want to remove the instance of this module from %s folder?"
18
+ "Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>","Manage the order from here by adding <a href='%s' target='_blank'>action buttons</a>"
19
+ "http://en.e-abi.ee/magento/better-order-management.html","http://en.e-abi.ee/magento/better-order-management.html"
js/eabi_js/crossBrowser_initKeyboardEvent.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Podium = {};
3
+ Podium.keydown = function(k) {
4
+ var oEvent = document.createEvent('KeyboardEvent');
5
+
6
+ // Chromium Hack
7
+ Object.defineProperty(oEvent, 'keyCode', {
8
+ get : function() {
9
+ return this.keyCodeVal;
10
+ }
11
+ });
12
+ Object.defineProperty(oEvent, 'which', {
13
+ get : function() {
14
+ return this.keyCodeVal;
15
+ }
16
+ });
17
+
18
+ if (oEvent.initKeyboardEvent) {
19
+ oEvent.initKeyboardEvent("keydown", true, true, document.defaultView, false, false, false, false, k, k);
20
+ } else {
21
+ oEvent.initKeyEvent("keydown", true, true, document.defaultView, false, false, false, false, k, 0);
22
+ }
23
+
24
+ oEvent.keyCodeVal = k;
25
+
26
+ if (oEvent.keyCode !== k) {
27
+ alert("keyCode mismatch " + oEvent.keyCode + "(" + oEvent.which + ")");
28
+ }
29
+
30
+ document.dispatchEvent(oEvent);
31
+ }
js/livepipe/contextmenu.js ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/contextmenu
7
+ * @require prototype.js, livepipe.js
8
+ */
9
+
10
+ /*global window, document, Prototype, Class, Event, $, $A, $R, Control, $value */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "Control.ContextMenu requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "Control.ContextMenu requires Object.Event to be loaded."; }
16
+
17
+ Control.ContextMenu = Class.create({
18
+ initialize: function(container,options){
19
+ Control.ContextMenu.load();
20
+ this.options = Object.extend({
21
+ leftClick: false,
22
+ disableOnShiftKey: true,
23
+ disableOnAltKey: true,
24
+ selectedClassName: 'selected',
25
+ activatedClassName: 'activated',
26
+ animation: true,
27
+ animationCycles: 2,
28
+ animationLength: 300,
29
+ delayCallback: true
30
+ },options || {});
31
+ this.activated = false;
32
+ this.items = this.options.items || [];
33
+ this.container = $(container);
34
+ this.container.observe(this.options.leftClick ? 'click' : (Prototype.Browser.Opera ? 'click' : 'contextmenu'),function(event){
35
+ if(!Control.ContextMenu.enabled || Prototype.Browser.Opera && !event.ctrlKey) {
36
+ return; }
37
+ this.open(event);
38
+ }.bindAsEventListener(this));
39
+ },
40
+ open: function(event){
41
+ if(Control.ContextMenu.current && !Control.ContextMenu.current.close()) {
42
+ return; }
43
+ if(this.notify('beforeOpen',event) === false) {
44
+ return false; }
45
+ this.buildMenu();
46
+ if(this.items.length === 0){
47
+ this.close(event);
48
+ return false;
49
+ }
50
+ this.clicked = Event.element(event);
51
+ Control.ContextMenu.current = this;
52
+ Control.ContextMenu.positionContainer(event);
53
+ Control.ContextMenu.container.show();
54
+ if(this.notify('afterOpen',event) === false) {
55
+ return false; }
56
+ event.stop();
57
+ return true;
58
+ },
59
+ close: function(event){
60
+ if(event) {
61
+ event.stop(); }
62
+ if(this.notify('beforeClose') === false) {
63
+ return false; }
64
+ Control.ContextMenu.current = false;
65
+ this.activated = false;
66
+ Control.ContextMenu.container.removeClassName(this.options.activatedClassName);
67
+ Control.ContextMenu.container.select('li').invoke('stopObserving');
68
+ Control.ContextMenu.container.hide();
69
+ Control.ContextMenu.container.update('');
70
+ if(this.notify('afterClose') === false) {
71
+ return false; }
72
+ return true;
73
+ },
74
+ buildMenu: function(){
75
+ var list = document.createElement('ul');
76
+ Control.ContextMenu.container.appendChild(list);
77
+ this.items.each(function(item){
78
+ if(!(!item.condition || item.condition && item.condition() !== false)) {
79
+ return; }
80
+ var item_container = $(document.createElement('li'));
81
+ item_container.update($value(item.label));
82
+ list.appendChild(item_container);
83
+ item_container[$value(item.enabled) ? 'removeClassName' : 'addClassName']('disabled');
84
+ item_container.observe('mousedown',function(event,item){
85
+ if(!$value(item.enabled)) {
86
+ return event.stop(); }
87
+ this.activated = $value(item.label);
88
+ }.bindAsEventListener(this,item));
89
+ item_container.observe('click',this.selectMenuItem.bindAsEventListener(this,item,item_container));
90
+ item_container.observe('contextmenu',this.selectMenuItem.bindAsEventListener(this,item,item_container));
91
+ }.bind(this));
92
+ },
93
+ addItem: function(params){
94
+ if (!('enabled' in params)) { params.enabled = true; }
95
+ this.items.push(params);
96
+ return this;
97
+ },
98
+ destroy: function(){
99
+ this.container.stopObserving(Prototype.Browser.Opera || this.options.leftClick ? 'click' : 'contextmenu');
100
+ this.items = [];
101
+ },
102
+ selectMenuItem: function(event,item,item_container){
103
+ if(!$value(item.enabled)) {
104
+ return event.stop(); }
105
+ if(!this.activated || this.activated == $value(item.label)){
106
+ if(this.options.animation){
107
+ Control.ContextMenu.container.addClassName(this.options.activatedClassName);
108
+ $A($R(0,this.options.animationCycles * 2)).each(function(i){
109
+ window.setTimeout(function(){
110
+ item_container.toggleClassName(this.options.selectedClassName);
111
+ }.bind(this),i * parseInt(this.options.animationLength / (this.options.animationCycles * 2), 10));
112
+ }.bind(this));
113
+ window.setTimeout(function(){
114
+ if(this.close() && this.options.delayCallback) {
115
+ item.callback(this.clicked); }
116
+ }.bind(this),this.options.animationLength);
117
+ if(!this.options.delayCallback) {
118
+ item.callback(this.clicked); }
119
+ }else if(this.close()) {
120
+ item.callback(this.clicked); }
121
+ }
122
+ event.stop();
123
+ return false;
124
+ }
125
+ });
126
+ Object.extend(Control.ContextMenu,{
127
+ loaded: false,
128
+ capture_all: false,
129
+ menus: [],
130
+ current: false,
131
+ enabled: false,
132
+ offset: 4,
133
+ load: function(capture_all){
134
+ if(Control.ContextMenu.loaded) {
135
+ return; }
136
+ Control.ContextMenu.loaded = true;
137
+ if(typeof(capture_all) == 'undefined') {
138
+ capture_all = false; }
139
+ Control.ContextMenu.capture_all = capture_all;
140
+ Control.ContextMenu.container = $(document.createElement('div'));
141
+ Control.ContextMenu.container.id = 'control_contextmenu';
142
+ Control.ContextMenu.container.style.position = 'absolute';
143
+ Control.ContextMenu.container.style.zIndex = 99999;
144
+ Control.ContextMenu.container.hide();
145
+ document.body.appendChild(Control.ContextMenu.container);
146
+ Control.ContextMenu.enable();
147
+ },
148
+ enable: function(){
149
+ Control.ContextMenu.enabled = true;
150
+ Event.observe(document.body,'click',Control.ContextMenu.onClick);
151
+ if(Control.ContextMenu.capture_all) {
152
+ Event.observe(document.body,'contextmenu',Control.ContextMenu.onContextMenu); }
153
+ },
154
+ disable: function(){
155
+ Event.stopObserving(document.body,'click',Control.ContextMenu.onClick);
156
+ if(Control.ContextMenu.capture_all) {
157
+ Event.stopObserving(document.body,'contextmenu',Control.ContextMenu.onContextMenu); }
158
+ },
159
+ onContextMenu: function(event){
160
+ event.stop();
161
+ return false;
162
+ },
163
+ onClick: function(){
164
+ if(Control.ContextMenu.current) {
165
+ Control.ContextMenu.current.close(); }
166
+ },
167
+ positionContainer: function(event){
168
+ var dimensions = Control.ContextMenu.container.getDimensions();
169
+ var top = Event.pointerY(event);
170
+ var left = Event.pointerX(event);
171
+ var bottom = dimensions.height + top;
172
+ var right = dimensions.width + left;
173
+ var viewport_dimensions = document.viewport.getDimensions();
174
+ var viewport_scroll_offsets = document.viewport.getScrollOffsets();
175
+ if(bottom > viewport_dimensions.height + viewport_scroll_offsets.top) {
176
+ top -= bottom - ((viewport_dimensions.height + viewport_scroll_offsets.top) - Control.ContextMenu.offset); }
177
+ if(right > viewport_dimensions.width + viewport_scroll_offsets.left) {
178
+ left -= right - ((viewport_dimensions.width + viewport_scroll_offsets.left) - Control.ContextMenu.offset); }
179
+ Control.ContextMenu.container.setStyle({
180
+ top: top + 'px',
181
+ left: left + 'px'
182
+ });
183
+ }
184
+ });
185
+ Object.Event.extend(Control.ContextMenu);
js/livepipe/cookie.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/controls/hotkey/
7
+ * @attribution http://www.quirksmode.org/js/cookies.html
8
+ */
9
+
10
+ /*global document, Prototype, $A */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "Cookie requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "Cookie requires Object.Event to be loaded."; }
16
+
17
+ var Cookie = {
18
+ build: function() {
19
+ return $A(arguments).compact().join("; ");
20
+ },
21
+ secondsFromNow: function(seconds) {
22
+ var d = new Date();
23
+ d.setTime(d.getTime() + (seconds * 1000));
24
+ return d.toGMTString();
25
+ },
26
+ set: function(name,value,seconds){
27
+ Cookie.notify('set',name,value);
28
+ var expiry = seconds ? 'expires=' + Cookie.secondsFromNow(seconds) : null;
29
+ document.cookie = Cookie.build(name + "=" + value, expiry, "path=/");
30
+ },
31
+ get: function(name){
32
+ Cookie.notify('get',name);
33
+ var valueMatch = new RegExp(name + "=([^;]+)").exec(document.cookie);
34
+ return valueMatch ? valueMatch[1] : null;
35
+ },
36
+ unset: function(name){
37
+ Cookie.notify('unset',name);
38
+ Cookie.set(name,'',-1);
39
+ }
40
+ };
41
+ Object.Event.extend(Cookie);
js/livepipe/event_behavior.js ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/extra/event_behavior
7
+ * @require prototype.js, livepipe.js
8
+ * @attribution http://www.adamlogic.com/2007/03/20/3_metaprogramming-javascript-presentation
9
+ */
10
+
11
+ /*global Prototype, Class, Event, Try, $, $A, $H */
12
+
13
+ if(typeof(Prototype) == "undefined") {
14
+ throw "Event.Behavior requires Prototype to be loaded."; }
15
+ if(typeof(Object.Event) == "undefined") {
16
+ throw "Event.Behavior requires Object.Event to be loaded."; }
17
+
18
+ Event.Behavior = {
19
+ addVerbs: function(verbs){
20
+ var v;
21
+ for (var name in verbs) { if (verbs.hasOwnProperty(name)) {
22
+ v = new Event.Behavior.Verb(verbs[name]);
23
+ Event.Behavior.Verbs[name] = v;
24
+ Event.Behavior[name.underscore()] = Event.Behavior[name] = v.getCallbackForStack.bind(v);
25
+ }}
26
+ },
27
+ addEvents: function(events){
28
+ $H(events).each(function(event_type){
29
+ Event.Behavior.Adjective.prototype[event_type.key.underscore()] = Event.Behavior.Adjective.prototype[event_type.key] = function(){
30
+ this.nextConditionType = 'and';
31
+ this.events.push(event_type.value);
32
+ this.attachObserver(false);
33
+ return this;
34
+ };
35
+ });
36
+ },
37
+ invokeElementMethod: function(element,action,args){
38
+ if(typeof(element) == 'function'){
39
+ return $A(element()).each(function(e){
40
+ if(typeof(args[0]) == 'function'){
41
+ return $A(args[0]).each(function(a){
42
+ return $(e)[action].apply($(e),(a ? [a] : []));
43
+ });
44
+ }else {
45
+ return $(e)[action].apply($(e),args || []); }
46
+ });
47
+ }else {
48
+ return $(element)[action].apply($(element),args || []); }
49
+ }
50
+ };
51
+
52
+ Event.Behavior.Verbs = $H({});
53
+
54
+ Event.Behavior.Verb = Class.create();
55
+ Object.extend(Event.Behavior.Verb.prototype,{
56
+ originalAction: false,
57
+ execute: false,
58
+ executeOpposite: false,
59
+ target: false,
60
+ initialize: function(action){
61
+ this.originalAction = action;
62
+ this.execute = function(action,target,argument){
63
+ return (argument) ? action(target,argument) : action(target);
64
+ }.bind(this,action);
65
+ },
66
+ setOpposite: function(opposite_verb){
67
+ var opposite_action = opposite_verb.originalAction;
68
+ this.executeOpposite = function(opposite_action,target,argument){
69
+ return (argument) ? opposite_action(target,argument) :
70
+ opposite_action(target);
71
+ }.bind(this,opposite_action);
72
+ },
73
+ getCallbackForStack: function(argument){
74
+ return new Event.Behavior.Noun(this,argument);
75
+ }
76
+ });
77
+
78
+ Event.Behavior.addVerbs({
79
+ call: function(callback){
80
+ callback();
81
+ },
82
+ show: function(element){
83
+ return Event.Behavior.invokeElementMethod(element,'show');
84
+ },
85
+ hide: function(element){
86
+ return Event.Behavior.invokeElementMethod(element,'hide');
87
+ },
88
+ remove: function(element){
89
+ return Event.Behavior.invokeElementMethod(element,'remove');
90
+ },
91
+ setStyle: function(element,styles){
92
+ return Event.Behavior.invokeElementMethod(element,'setStyle',[(typeof(styles) == 'function' ? styles() : styles)]);
93
+ },
94
+ addClassName: function(element,class_name){
95
+ return Event.Behavior.invokeElementMethod(element,'addClassName',[(typeof(class_name) == 'function' ? class_name() : class_name)]);
96
+ },
97
+ removeClassName: function(element,class_name){
98
+ return Event.Behavior.invokeElementMethod(element,'removeClassName',[(typeof(class_name) == 'function' ? class_name() : class_name)]);
99
+ },
100
+ setClassName: function(element,class_name){
101
+ var c = (typeof(class_name) == 'function') ? class_name() : class_name;
102
+ if(typeof(element) == 'function'){
103
+ return $A(element()).each(function(e){
104
+ $(e).className = c;
105
+ });
106
+ }else {
107
+ c = $(element).className;
108
+ return c;
109
+ }
110
+ },
111
+ update: function(content,element){
112
+ return Event.Behavior.invokeElementMethod(element,'update',[(typeof(content) == 'function' ? content() : content)]);
113
+ },
114
+ replace: function(content,element){
115
+ return Event.Behavior.invokeElementMethod(element,'replace',[(typeof(content) == 'function' ? content() : content)]);
116
+ }
117
+ });
118
+ Event.Behavior.Verbs.show.setOpposite(Event.Behavior.Verbs.hide);
119
+ Event.Behavior.Verbs.hide.setOpposite(Event.Behavior.Verbs.show);
120
+ Event.Behavior.Verbs.addClassName.setOpposite(Event.Behavior.Verbs.removeClassName);
121
+ Event.Behavior.Verbs.removeClassName.setOpposite(Event.Behavior.Verbs.addClassName);
122
+
123
+ Event.Behavior.Noun = Class.create();
124
+ Object.extend(Event.Behavior.Noun.prototype,{
125
+ verbs: false,
126
+ verb: false,
127
+ argument: false,
128
+ subject: false,
129
+ target: false,
130
+ initialize: function(verb,argument){
131
+ //this.verbs = $A([]);
132
+ this.verb = verb;
133
+ this.argument = argument;
134
+ },
135
+ execute: function(){
136
+ return (this.target) ? this.verb.execute(this.target,this.argument) :
137
+ this.verb.execute(this.argument);
138
+ },
139
+ executeOpposite: function(){
140
+ return (this.target) ?
141
+ this.verb.executeOpposite(this.target,this.argument) :
142
+ this.verb.executeOpposite(this.argument);
143
+ },
144
+ when: function(subject){
145
+ this.subject = subject;
146
+ return new Event.Behavior.Adjective(this);
147
+ },
148
+ getValue: function(){
149
+ return Try.these(
150
+ function(){return $(this.subject).getValue();}.bind(this),
151
+ function(){return $(this.subject).options[$(this.subject).options.selectedIndex].value;}.bind(this),
152
+ function(){return $(this.subject).value;}.bind(this),
153
+ function(){return $(this.subject).innerHTML;}.bind(this)
154
+ );
155
+ },
156
+ containsValue: function(match){
157
+ var value = this.getValue();
158
+ if(typeof(match) == 'function'){
159
+ return $A(match()).include(value);
160
+ }else {
161
+ return value.match(match); }
162
+ },
163
+ setTarget: function(target){
164
+ this.target = target;
165
+ return this;
166
+ },
167
+ and: function(){
168
+
169
+ }
170
+ });
171
+ Event.Behavior.Noun.prototype._with = Event.Behavior.Noun.prototype.setTarget;
172
+ Event.Behavior.Noun.prototype.on = Event.Behavior.Noun.prototype.setTarget;
173
+ Event.Behavior.Noun.prototype.of = Event.Behavior.Noun.prototype.setTarget;
174
+ Event.Behavior.Noun.prototype.to = Event.Behavior.Noun.prototype.setTarget;
175
+ Event.Behavior.Noun.prototype.from = Event.Behavior.Noun.prototype.setTarget;
176
+
177
+ Event.Behavior.Adjective = Class.create();
178
+ Object.extend(Event.Behavior.Adjective.prototype,{
179
+ noun: false,
180
+ lastConditionName: '',
181
+ nextConditionType: 'and',
182
+ conditions: $A([]),
183
+ events: $A([]),
184
+ attached: false,
185
+ initialize: function(noun){
186
+ this.conditions = $A([]);
187
+ this.events = $A([]);
188
+ this.noun = noun;
189
+ },
190
+ attachObserver: function(execute_on_load){
191
+ if(this.attached){
192
+ //this may call things multiple times, but is the only way to gaurentee correct state on startup
193
+ if(execute_on_load) {
194
+ this.execute(); }
195
+ return;
196
+ }
197
+ this.attached = true;
198
+ if(typeof(this.noun.subject) == 'function'){
199
+ $A(this.noun.subject()).each(function(subject){
200
+ (this.events.length > 0 ? this.events : $A(['change'])).each(function(event_name){
201
+ (subject.observe ? subject : $(subject)).observe(event_name,function(){
202
+ this.execute();
203
+ }.bind(this));
204
+ }.bind(this));
205
+ }.bind(this));
206
+ }else{
207
+ (this.events.length > 0 ? this.events : $A(['change'])).each(function(event_name){
208
+ $(this.noun.subject).observe(event_name,function(){
209
+ this.execute();
210
+ }.bind(this));
211
+ }.bind(this));
212
+ }
213
+ if(execute_on_load) { this.execute(); }
214
+ },
215
+ execute: function(){
216
+ if(this.match()) { return this.noun.execute(); }
217
+ else if(this.noun.verb.executeOpposite) { this.noun.executeOpposite(); }
218
+ },
219
+ attachCondition: function(callback){
220
+ this.conditions.push([this.nextConditionType,callback.bind(this)]);
221
+ },
222
+ match: function(){
223
+ if(this.conditions.length === 0) { return true; }
224
+ else {
225
+ return this.conditions.inject(false, function (bool,condition) {
226
+ return (condition[0] === 'or') ?
227
+ (bool && condition[1]()) : (bool || condition[1]());
228
+ });
229
+ }
230
+ },
231
+ //conditions
232
+ is: function(item){
233
+ this.lastConditionName = 'is';
234
+ this.attachCondition(function(item){
235
+ return (typeof(item) == 'function' ? item() : item) == this.noun.getValue();
236
+ }.bind(this,item));
237
+ this.attachObserver(true);
238
+ return this;
239
+ },
240
+ isNot: function(item){
241
+ this.lastConditionName = 'isNot';
242
+ this.attachCondition(function(item){
243
+ return (typeof(item) == 'function' ? item() : item) != this.noun.getValue();
244
+ }.bind(this,item));
245
+ this.attachObserver(true);
246
+ return this;
247
+ },
248
+ contains: function(item){
249
+ this.lastConditionName = 'contains';
250
+ this.attachCondition(function(item){
251
+ return this.noun.containsValue(item);
252
+ }.bind(this,item));
253
+ this.attachObserver(true);
254
+ return this;
255
+ },
256
+ within: function(item){
257
+ this.lastConditionName = 'within';
258
+ this.attachCondition(function(item){
259
+
260
+ }.bind(this,item));
261
+ this.attachObserver(true);
262
+ return this;
263
+ },
264
+ //events
265
+ change: function(){
266
+ this.nextConditionType = 'and';
267
+ this.attachObserver(true);
268
+ return this;
269
+ },
270
+ and: function(condition){
271
+ this.attached = false;
272
+ this.nextConditionType = 'and';
273
+ if(condition) { this[this.lastConditionName](condition); }
274
+ return this;
275
+ },
276
+ or: function(condition){
277
+ this.attached = false;
278
+ this.nextConditionType = 'or';
279
+ if(condition) { this[this.lastConditionName](condition); }
280
+ return this;
281
+ }
282
+ });
283
+
284
+ Event.Behavior.addEvents({
285
+ losesFocus: 'blur',
286
+ gainsFocus: 'focus',
287
+ isClicked: 'click',
288
+ isDoubleClicked: 'dblclick',
289
+ keyPressed: 'keypress'
290
+ });
291
+
292
+ Event.Behavior.Adjective.prototype.is_not = Event.Behavior.Adjective.prototype.isNot;
293
+ Event.Behavior.Adjective.prototype.include = Event.Behavior.Adjective.prototype.contains;
294
+ Event.Behavior.Adjective.prototype.includes = Event.Behavior.Adjective.prototype.contains;
295
+ Event.Behavior.Adjective.prototype.are = Event.Behavior.Adjective.prototype.is;
296
+ Event.Behavior.Adjective.prototype.areNot = Event.Behavior.Adjective.prototype.isNot;
297
+ Event.Behavior.Adjective.prototype.are_not = Event.Behavior.Adjective.prototype.isNot;
298
+ Event.Behavior.Adjective.prototype.changes = Event.Behavior.Adjective.prototype.change;
js/livepipe/hotkey.js ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/extra/hotkey
7
+ * @require prototype.js, livepipe.js
8
+ */
9
+
10
+ /*global document, Prototype, Class, Event, $ */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "HotKey requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "HotKey requires Object.Event to be loaded."; }
16
+
17
+ var HotKey = Class.create({
18
+ initialize: function(letter,callback,options){
19
+ letter = letter.toUpperCase();
20
+ HotKey.hotkeys.push(this);
21
+ this.options = Object.extend({
22
+ element: false,
23
+ shiftKey: false,
24
+ altKey: false,
25
+ ctrlKey: true,
26
+ bubbleEvent : true,
27
+ fireOnce : false // Keep repeating event while key is pressed?
28
+ },options || {});
29
+ this.letter = letter;
30
+
31
+ // All custom hotkey events should stop after their custom actions.
32
+ this.callback = function (event) {
33
+ if (!(this.options.fireOnce && this.fired) && Object.isFunction(callback)) {
34
+ callback(event);
35
+ }
36
+ if (!this.options.bubbleEvent) { event.stop(); }
37
+ this.fired = true;
38
+ };
39
+
40
+ this.element = $(this.options.element || document);
41
+ this.handler = function(event){
42
+ if(!event || (
43
+ (Event['KEY_' + this.letter] || this.letter.charCodeAt(0)) == event.keyCode &&
44
+ ((!this.options.shiftKey || (this.options.shiftKey && event.shiftKey)) &&
45
+ (!this.options.altKey || (this.options.altKey && event.altKey)) &&
46
+ (!this.options.ctrlKey || (this.options.ctrlKey && event.ctrlKey))
47
+ )
48
+ )){
49
+ if(this.notify('beforeCallback',event) === false) {
50
+ return; }
51
+ this.callback(event);
52
+ this.notify('afterCallback',event);
53
+ }
54
+ }.bind(this);
55
+ this.enable();
56
+ },
57
+ trigger: function(){
58
+ this.handler();
59
+ },
60
+ enable: function(){
61
+ this.element.observe('keydown',this.handler);
62
+ },
63
+ disable: function(){
64
+ this.element.stopObserving('keydown',this.handler);
65
+ },
66
+ destroy: function(){
67
+ this.disable();
68
+ HotKey.hotkeys = HotKey.hotkeys.without(this);
69
+ }
70
+ });
71
+ Object.extend(HotKey,{
72
+ hotkeys: []
73
+ });
74
+ Object.Event.extend(HotKey);
js/livepipe/livepipe.js ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/core
7
+ * @require prototype.js
8
+ */
9
+
10
+ if(typeof(Control) == 'undefined')
11
+ Control = {};
12
+
13
+ var $proc = function(proc){
14
+ return typeof(proc) == 'function' ? proc : function(){return proc};
15
+ };
16
+
17
+ var $value = function(value){
18
+ return typeof(value) == 'function' ? value() : value;
19
+ };
20
+
21
+ Object.Event = {
22
+ extend: function(object){
23
+ object._objectEventSetup = function(event_name){
24
+ this._observers = this._observers || {};
25
+ this._observers[event_name] = this._observers[event_name] || [];
26
+ };
27
+ object.observe = function(event_name,observer){
28
+ if(typeof(event_name) == 'string' && typeof(observer) != 'undefined'){
29
+ this._objectEventSetup(event_name);
30
+ if(!this._observers[event_name].include(observer))
31
+ this._observers[event_name].push(observer);
32
+ }else
33
+ for(var e in event_name)
34
+ this.observe(e,event_name[e]);
35
+ };
36
+ object.stopObserving = function(event_name,observer){
37
+ this._objectEventSetup(event_name);
38
+ if(event_name && observer)
39
+ this._observers[event_name] = this._observers[event_name].without(observer);
40
+ else if(event_name)
41
+ this._observers[event_name] = [];
42
+ else
43
+ this._observers = {};
44
+ };
45
+ object.observeOnce = function(event_name,outer_observer){
46
+ var inner_observer = function(){
47
+ outer_observer.apply(this,arguments);
48
+ this.stopObserving(event_name,inner_observer);
49
+ }.bind(this);
50
+ this._objectEventSetup(event_name);
51
+ this._observers[event_name].push(inner_observer);
52
+ };
53
+ object.notify = function(event_name){
54
+ this._objectEventSetup(event_name);
55
+ var collected_return_values = [];
56
+ var args = $A(arguments).slice(1);
57
+ try{
58
+ for(var i = 0; i < this._observers[event_name].length; ++i)
59
+ collected_return_values.push(this._observers[event_name][i].apply(this,args) || null);
60
+ }catch(e){
61
+ if(e == $break)
62
+ return false;
63
+ else
64
+ throw e;
65
+ }
66
+ return collected_return_values;
67
+ };
68
+ if(object.prototype){
69
+ object.prototype._objectEventSetup = object._objectEventSetup;
70
+ object.prototype.observe = object.observe;
71
+ object.prototype.stopObserving = object.stopObserving;
72
+ object.prototype.observeOnce = object.observeOnce;
73
+ object.prototype.notify = function(event_name){
74
+ if(object.notify){
75
+ var args = $A(arguments).slice(1);
76
+ args.unshift(this);
77
+ args.unshift(event_name);
78
+ object.notify.apply(object,args);
79
+ }
80
+ this._objectEventSetup(event_name);
81
+ var args = $A(arguments).slice(1);
82
+ var collected_return_values = [];
83
+ try{
84
+ if(this.options && this.options[event_name] && typeof(this.options[event_name]) == 'function')
85
+ collected_return_values.push(this.options[event_name].apply(this,args) || null);
86
+ var callbacks_copy = this._observers[event_name]; // since original array will be modified after observeOnce calls
87
+ for(var i = 0; i < callbacks_copy.length; ++i)
88
+ collected_return_values.push(callbacks_copy[i].apply(this,args) || null);
89
+ }catch(e){
90
+ if(e == $break)
91
+ return false;
92
+ else
93
+ throw e;
94
+ }
95
+ return collected_return_values;
96
+ };
97
+ }
98
+ }
99
+ };
100
+
101
+ /* Begin Core Extensions */
102
+
103
+ //Element.observeOnce
104
+ Element.addMethods({
105
+ observeOnce: function(element,event_name,outer_callback){
106
+ var inner_callback = function(){
107
+ outer_callback.apply(this,arguments);
108
+ Element.stopObserving(element,event_name,inner_callback);
109
+ };
110
+ Element.observe(element,event_name,inner_callback);
111
+ }
112
+ });
113
+
114
+ //mouse:wheel
115
+ (function(){
116
+ function wheel(event){
117
+ var delta, element, custom_event;
118
+ // normalize the delta
119
+ if (event.wheelDelta) { // IE & Opera
120
+ delta = event.wheelDelta / 120;
121
+ } else if (event.detail) { // W3C
122
+ delta =- event.detail / 3;
123
+ }
124
+ if (!delta) { return; }
125
+ element = Event.extend(event).target;
126
+ element = Element.extend(element.nodeType === Node.TEXT_NODE ? element.parentNode : element);
127
+ custom_event = element.fire('mouse:wheel',{ delta: delta });
128
+ if (custom_event.stopped) {
129
+ Event.stop(event);
130
+ return false;
131
+ }
132
+ }
133
+ document.observe('mousewheel',wheel);
134
+ document.observe('DOMMouseScroll',wheel);
135
+ })();
136
+
137
+ /* End Core Extensions */
138
+
139
+ //from PrototypeUI
140
+ var IframeShim = Class.create({
141
+ initialize: function() {
142
+ this.element = new Element('iframe',{
143
+ style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);display:none',
144
+ src: 'javascript:false;',
145
+ frameborder: 0
146
+ });
147
+ $(document.body).insert(this.element);
148
+ },
149
+ hide: function() {
150
+ this.element.hide();
151
+ return this;
152
+ },
153
+ show: function() {
154
+ this.element.show();
155
+ return this;
156
+ },
157
+ positionUnder: function(element) {
158
+ var element = $(element);
159
+ var offset = element.cumulativeOffset();
160
+ var dimensions = element.getDimensions();
161
+ this.element.setStyle({
162
+ left: offset[0] + 'px',
163
+ top: offset[1] + 'px',
164
+ width: dimensions.width + 'px',
165
+ height: dimensions.height + 'px',
166
+ zIndex: element.getStyle('zIndex') - 1
167
+ }).show();
168
+ return this;
169
+ },
170
+ setBounds: function(bounds) {
171
+ for(prop in bounds)
172
+ bounds[prop] += 'px';
173
+ this.element.setStyle(bounds);
174
+ return this;
175
+ },
176
+ destroy: function() {
177
+ if(this.element)
178
+ this.element.remove();
179
+ return this;
180
+ }
181
+ });
js/livepipe/progressbar.js ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/progressbar
7
+ * @require prototype.js, livepipe.js
8
+ */
9
+
10
+ /*global document, Prototype, Ajax, Class, PeriodicalExecuter, $, $A, Control */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "Control.ProgressBar requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "Control.ProgressBar requires Object.Event to be loaded."; }
16
+
17
+ Control.ProgressBar = Class.create({
18
+ initialize: function(container,options){
19
+ this.progress = 0;
20
+ this.executer = false;
21
+ this.active = false;
22
+ this.poller = false;
23
+ this.container = $(container);
24
+ this.containerWidth = this.container.getDimensions().width - (parseInt(this.container.getStyle('border-right-width').replace(/px/,''), 10) + parseInt(this.container.getStyle('border-left-width').replace(/px/,''), 10));
25
+ this.progressContainer = $(document.createElement('div'));
26
+ this.progressContainer.setStyle({
27
+ width: this.containerWidth + 'px',
28
+ height: '100%',
29
+ position: 'absolute',
30
+ top: '0px',
31
+ right: '0px'
32
+ });
33
+ this.container.appendChild(this.progressContainer);
34
+ this.options = {
35
+ afterChange: Prototype.emptyFunction,
36
+ interval: 0.25,
37
+ step: 1,
38
+ classNames: {
39
+ active: 'progress_bar_active',
40
+ inactive: 'progress_bar_inactive'
41
+ }
42
+ };
43
+ Object.extend(this.options,options || {});
44
+ this.container.addClassName(this.options.classNames.inactive);
45
+ this.active = false;
46
+ },
47
+ setProgress: function(value){
48
+ this.progress = value;
49
+ this.draw();
50
+ if(this.progress >= 100) {
51
+ this.stop(false); }
52
+ this.notify('afterChange',this.progress,this.active);
53
+ },
54
+ poll: function (url, interval, ajaxOptions){
55
+ // Extend the passed ajax options and success callback with our own.
56
+ ajaxOptions = ajaxOptions || {};
57
+ var success = ajaxOptions.onSuccess || Prototype.emptyFunction;
58
+ ajaxOptions.onSuccess = success.wrap(function (callOriginal, request) {
59
+ this.setProgress(parseInt(request.responseText, 10));
60
+ if(!this.active) { this.poller.stop(); }
61
+ callOriginal(request);
62
+ }).bind(this);
63
+
64
+ this.active = true;
65
+ this.poller = new PeriodicalExecuter(function(){
66
+ var a = new Ajax.Request(url, ajaxOptions);
67
+ }.bind(this),interval || 3);
68
+ },
69
+ start: function(){
70
+ this.active = true;
71
+ this.container.removeClassName(this.options.classNames.inactive);
72
+ this.container.addClassName(this.options.classNames.active);
73
+ this.executer = new PeriodicalExecuter(this.step.bind(this,this.options.step),this.options.interval);
74
+ },
75
+ stop: function(reset){
76
+ this.active = false;
77
+ if(this.executer) {
78
+ this.executer.stop(); }
79
+ this.container.removeClassName(this.options.classNames.active);
80
+ this.container.addClassName(this.options.classNames.inactive);
81
+ if (typeof reset === 'undefined' || reset === true) {
82
+ this.reset(); }
83
+ },
84
+ step: function(amount){
85
+ this.active = true;
86
+ this.setProgress(Math.min(100,this.progress + amount));
87
+ },
88
+ reset: function(){
89
+ this.active = false;
90
+ this.setProgress(0);
91
+ },
92
+ draw: function(){
93
+ this.progressContainer.setStyle({
94
+ width: (this.containerWidth - Math.floor((parseInt(this.progress, 10) / 100) * this.containerWidth)) + 'px'
95
+ });
96
+ },
97
+ notify: function(event_name){
98
+ if(this.options[event_name]) {
99
+ return [this.options[event_name].apply(this.options[event_name],$A(arguments).slice(1))]; }
100
+ }
101
+ });
102
+ Object.Event.extend(Control.ProgressBar);
js/livepipe/rating.js ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/rating
7
+ * @require prototype.js, livepipe.js
8
+ */
9
+
10
+ /*global document, Prototype, Ajax, Class, Event, $, $A, $F, $R, $break, Control */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "Control.Rating requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "Control.Rating requires Object.Event to be loaded."; }
16
+
17
+ Control.Rating = Class.create({
18
+ initialize: function(container,options){
19
+ Control.Rating.instances.push(this);
20
+ this.value = false;
21
+ this.links = [];
22
+ this.container = $(container);
23
+ this.container.update('');
24
+ this.options = {
25
+ min: 1,
26
+ max: 5,
27
+ rated: false,
28
+ input: false,
29
+ reverse: false,
30
+ capture: true,
31
+ multiple: false,
32
+ classNames: {
33
+ off: 'rating_off',
34
+ half: 'rating_half',
35
+ on: 'rating_on',
36
+ selected: 'rating_selected'
37
+ },
38
+ updateUrl: false,
39
+ updateParameterName: 'value',
40
+ updateOptions : {},
41
+ afterChange: Prototype.emptyFunction
42
+ };
43
+ Object.extend(this.options,options || {});
44
+ if(this.options.value){
45
+ this.value = this.options.value;
46
+ delete this.options.value;
47
+ }
48
+ if(this.options.input){
49
+ this.options.input = $(this.options.input);
50
+ this.options.input.observe('change',function(input){
51
+ this.setValueFromInput(input);
52
+ }.bind(this,this.options.input));
53
+ this.setValueFromInput(this.options.input,true);
54
+ }
55
+ var range = $R(this.options.min,this.options.max);
56
+ (this.options.reverse ? $A(range).reverse() : range).each(function(i){
57
+ var link = this.buildLink(i);
58
+ this.container.appendChild(link);
59
+ this.links.push(link);
60
+ }.bind(this));
61
+ this.setValue(this.value || this.options.min - 1,false,true);
62
+ },
63
+ buildLink: function(rating){
64
+ var link = $(document.createElement('a'));
65
+ link.value = rating;
66
+ if(this.options.multiple || (!this.options.rated && !this.options.multiple)){
67
+ link.href = '';
68
+ link.onmouseover = this.mouseOver.bind(this,link);
69
+ link.onmouseout = this.mouseOut.bind(this,link);
70
+ link.onclick = this.click.bindAsEventListener(this,link);
71
+ }else{
72
+ link.style.cursor = 'default';
73
+ link.observe('click',function(event){
74
+ Event.stop(event);
75
+ return false;
76
+ }.bindAsEventListener(this));
77
+ }
78
+ link.addClassName(this.options.classNames.off);
79
+ return link;
80
+ },
81
+ disable: function(){
82
+ this.links.each(function(link){
83
+ link.onmouseover = Prototype.emptyFunction;
84
+ link.onmouseout = Prototype.emptyFunction;
85
+ link.onclick = Prototype.emptyFunction;
86
+ link.observe('click',function(event){
87
+ Event.stop(event);
88
+ return false;
89
+ }.bindAsEventListener(this));
90
+ link.style.cursor = 'default';
91
+ }.bind(this));
92
+ },
93
+ setValueFromInput: function(input,prevent_callbacks){
94
+ this.setValue($F(input),true,prevent_callbacks);
95
+ },
96
+ setValue: function(value,force_selected,prevent_callbacks){
97
+ this.value = value;
98
+ if(this.options.input){
99
+ if(this.options.input.options){
100
+ $A(this.options.input.options).each(function(option,i){
101
+ if(option.value == this.value){
102
+ this.options.input.options.selectedIndex = i;
103
+ throw $break;
104
+ }
105
+ }.bind(this));
106
+ }else {
107
+ this.options.input.value = this.value; }
108
+ }
109
+ this.render(this.value,force_selected);
110
+ if(!prevent_callbacks){
111
+ if(this.options.updateUrl){
112
+ var params = {}, a;
113
+ params[this.options.updateParameterName] = this.value;
114
+ a = new Ajax.Request(this.options.updateUrl, Object.extend(
115
+ this.options.updateOptions, { parameters : params }
116
+ ));
117
+ }
118
+ this.notify('afterChange',this.value);
119
+ }
120
+ },
121
+ render: function(rating,force_selected){
122
+ (this.options.reverse ? this.links.reverse() : this.links).each(function(link,i){
123
+ if(link.value <= Math.ceil(rating)){
124
+ link.className = this.options.classNames[link.value <= rating ? 'on' : 'half'];
125
+ if(this.options.rated || force_selected) {
126
+ link.addClassName(this.options.classNames.selected); }
127
+ }else {
128
+ link.className = this.options.classNames.off; }
129
+ }.bind(this));
130
+ },
131
+ mouseOver: function(link){
132
+ this.render(link.value,true);
133
+ },
134
+ mouseOut: function(link){
135
+ this.render(this.value);
136
+ },
137
+ click: function(event,link){
138
+ this.options.rated = true;
139
+ this.setValue((link.value ? link.value : link),true);
140
+ if(!this.options.multiple) {
141
+ this.disable(); }
142
+ if(this.options.capture){
143
+ Event.stop(event);
144
+ return false;
145
+ }
146
+ }
147
+ });
148
+ Object.extend(Control.Rating,{
149
+ instances: [],
150
+ findByElementId: function(id){
151
+ return Control.Rating.instances.find(function(instance){
152
+ return (instance.container.id && instance.container.id == id);
153
+ });
154
+ }
155
+ });
156
+ Object.Event.extend(Control.Rating);
js/livepipe/resizable.js ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // script.aculo.us Resizables.js
3
+
4
+ // Copyright(c) 2007 - Orr Siloni, Comet Information Systems http://www.comet.co.il/en/
5
+ //
6
+ // Resizable.js is freely distributable under the terms of an MIT-style license.
7
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
8
+
9
+ var Resizables = {
10
+ instances: [],
11
+ observers: [],
12
+
13
+ register: function(resizable) {
14
+ if(this.instances.length == 0) {
15
+ this.eventMouseUp = this.endResize.bindAsEventListener(this);
16
+ this.eventMouseMove = this.updateResize.bindAsEventListener(this);
17
+
18
+ Event.observe(document, "mouseup", this.eventMouseUp);
19
+ Event.observe(document, "mousemove", this.eventMouseMove);
20
+ }
21
+ this.instances.push(resizable);
22
+ },
23
+
24
+ unregister: function(resizable) {
25
+ this.instances = this.instances.reject(function(d) { return d==resizable });
26
+ if(this.instances.length == 0) {
27
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
28
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
29
+ }
30
+ },
31
+
32
+ activate: function(resizable) {
33
+ if(resizable.options.delay) {
34
+ this._timeout = setTimeout(function() {
35
+ Resizables._timeout = null;
36
+ Resizables.activeResizable = resizable;
37
+ }.bind(this), resizable.options.delay);
38
+ } else {
39
+ this.activeResizable = resizable;
40
+ }
41
+ },
42
+
43
+ deactivate: function() {
44
+ this.activeResizable = null;
45
+ },
46
+
47
+ updateResize: function(event) {
48
+ if(!this.activeResizable) return;
49
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
50
+ // Mozilla-based browsers fire successive mousemove events with
51
+ // the same coordinates, prevent needless redrawing (moz bug?)
52
+ if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
53
+ this._lastPointer = pointer;
54
+
55
+ this.activeResizable.updateResize(event, pointer);
56
+ },
57
+
58
+ endResize: function(event) {
59
+ if(this._timeout) {
60
+ clearTimeout(this._timeout);
61
+ this._timeout = null;
62
+ }
63
+ if(!this.activeResizable) return;
64
+ this._lastPointer = null;
65
+ this.activeResizable.endResize(event);
66
+ this.activeResizable = null;
67
+ },
68
+
69
+ addObserver: function(observer) {
70
+ this.observers.push(observer);
71
+ this._cacheObserverCallbacks();
72
+ },
73
+
74
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
75
+ this.observers = this.observers.reject( function(o) { return o.element==element });
76
+ this._cacheObserverCallbacks();
77
+ },
78
+
79
+ notify: function(eventName, resizable, event) { // 'onStart', 'onEnd', 'onResize'
80
+ if(this[eventName+'Count'] > 0)
81
+ this.observers.each( function(o) {
82
+ if(o[eventName]) o[eventName](eventName, resizable, event);
83
+ });
84
+ if(resizable.options[eventName]) resizable.options[eventName](resizable, event);
85
+ },
86
+
87
+ _cacheObserverCallbacks: function() {
88
+ ['onStart','onEnd','onResize'].each( function(eventName) {
89
+ Resizables[eventName+'Count'] = Resizables.observers.select(
90
+ function(o) { return o[eventName]; }
91
+ ).length;
92
+ });
93
+ }
94
+ }
95
+
96
+ var Resizable = Class.create();
97
+ Resizable._resizing = {};
98
+
99
+ Resizable.prototype = {
100
+ initialize: function(element){
101
+ var defaults = {
102
+ handle: false,
103
+ snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
104
+ delay: 0,
105
+ minHeight: false,
106
+ minwidth: false,
107
+ maxHeight: false,
108
+ maxWidth: false
109
+ }
110
+
111
+ this.element = $(element);
112
+
113
+ var options = Object.extend(defaults, arguments[1] || {});
114
+ if(options.handle && typeof options.handle == 'string')
115
+ this.handle = $(options.handle);
116
+ else if(options.handle)
117
+ this.handle = options.handle;
118
+
119
+ if(!this.handle) this.handle = this.element;
120
+
121
+ this.options = options;
122
+ this.dragging = false;
123
+
124
+ this.eventMouseDown = this.initResize.bindAsEventListener(this);
125
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
126
+
127
+ Resizables.register(this);
128
+ },
129
+
130
+ destroy: function() {
131
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
132
+ },
133
+
134
+ currentDelta: function() {
135
+ return([
136
+ parseInt(Element.getStyle(this.element,'width') || '0'),
137
+ parseInt(Element.getStyle(this.element,'height') || '0')]);
138
+ },
139
+
140
+ initResize: function(event) {
141
+ if(typeof Resizable._resizing[this.element] != 'undefined' &&
142
+ Resizable._resizing[this.element]) return;
143
+ if(Event.isLeftClick(event)) {
144
+ // abort on form elements, fixes a Firefox issue
145
+ var src = Event.element(event);
146
+ if((tag_name = src.tagName.toUpperCase()) && (
147
+ tag_name=='INPUT' || tag_name=='SELECT' || tag_name=='OPTION' ||
148
+ tag_name=='BUTTON' || tag_name=='TEXTAREA')) return;
149
+
150
+ this.pointer = [Event.pointerX(event), Event.pointerY(event)];
151
+ this.size = [parseInt(this.element.getStyle('width')) || 0, parseInt(this.element.getStyle('height')) || 0];
152
+
153
+ Resizables.activate(this);
154
+ Event.stop(event);
155
+ }
156
+ },
157
+
158
+ startResize: function(event) {
159
+ this.resizing = true;
160
+ if(this.options.zindex) {
161
+ this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
162
+ this.element.style.zIndex = this.options.zindex;
163
+ }
164
+ Resizables.notify('onStart', this, event);
165
+ Resizable._resizing[this.element] = true;
166
+ },
167
+
168
+ updateResize: function(event, pointer) {
169
+ if(!this.resizing) this.startResize(event);
170
+
171
+ Resizables.notify('onResize', this, event);
172
+
173
+ this.draw(pointer);
174
+ if(this.options.change) this.options.change(this);
175
+
176
+ // fix AppleWebKit rendering
177
+ if(Prototype.Browser.WebKit) window.scrollBy(0,0);
178
+ Event.stop(event);
179
+ },
180
+
181
+ finishResize: function(event, success) {
182
+ this.resizing = false;
183
+ Resizables.notify('onEnd', this, event);
184
+ if(this.options.zindex) this.element.style.zIndex = this.originalZ;
185
+ Resizable._resizing[this.element] = false;
186
+ Resizables.deactivate(this);
187
+ },
188
+
189
+ endResize: function(event) {
190
+ if(!this.resizing) return;
191
+ this.finishResize(event, true);
192
+ Event.stop(event);
193
+ },
194
+
195
+ draw: function(point) {
196
+ var p = [0,1].map(function(i){
197
+ return (this.size[i] + point[i] - this.pointer[i]);
198
+ }.bind(this));
199
+
200
+ if(this.options.snap) {
201
+ if(typeof this.options.snap == 'function') {
202
+ p = this.options.snap(p[0],p[1],this);
203
+ } else {
204
+ if(this.options.snap instanceof Array) {
205
+ p = p.map( function(v, i) {
206
+ return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
207
+ } else {
208
+ p = p.map( function(v) {
209
+ return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
210
+ }
211
+ }}
212
+
213
+ var minWidth = (typeof(this.options.minWidth) == 'function') ? this.options.minWidth(this.element) : this.options.minWidth;
214
+ var maxWidth = (typeof(this.options.maxWidth) == 'function') ? this.options.maxWidth(this.element) : this.options.maxWidth;
215
+ var minHeight = (typeof(this.options.minHeight) == 'function') ? this.options.minHeight(this.element) : this.options.minHeight;
216
+ var maxHeight = (typeof(this.options.maxHeight) == 'function') ? this.options.maxHeight(this.element) : this.options.maxHeight;
217
+
218
+ if (minWidth && p[0] <= minWidth) p[0] = minWidth;
219
+ if (maxWidth && p[0] >= maxWidth) p[0] = maxWidth;
220
+ if (minHeight && p[1] <= minHeight) p[1] = minHeight;
221
+ if (maxHeight && p[1] >= maxHeight) p[1] = maxHeight;
222
+
223
+ var style = this.element.style;
224
+ if((!this.options.constraint) || (this.options.constraint=='horizontal')){
225
+ style.width = p[0] + "px";
226
+ }
227
+ if((!this.options.constraint) || (this.options.constraint=='vertical')){
228
+ style.height = p[1] + "px";
229
+ }
230
+
231
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
232
+ }
233
+ };
js/livepipe/scrollbar.js ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/scrollbar
7
+ * @require prototype.js, slider.js, livepipe.js
8
+ */
9
+
10
+ if(typeof(Prototype) == "undefined")
11
+ throw "Control.ScrollBar requires Prototype to be loaded.";
12
+ if(typeof(Control.Slider) == "undefined")
13
+ throw "Control.ScrollBar requires Control.Slider to be loaded.";
14
+ if(typeof(Object.Event) == "undefined")
15
+ throw "Control.ScrollBar requires Object.Event to be loaded.";
16
+
17
+ Control.ScrollBar = Class.create({
18
+ initialize: function(container,track,options){
19
+ Control.ScrollBar.instances.push(this);
20
+ this.enabled = false;
21
+ this.notificationTimeout = false;
22
+ this.container = $(container);
23
+ this.boundMouseWheelEvent = this.onMouseWheel.bindAsEventListener(this);
24
+ this.boundResizeObserver = this.onWindowResize.bind(this);
25
+ this.track = $(track);
26
+ this.handle = this.track.firstDescendant();
27
+ this.options = Object.extend({
28
+ active_class_name: 'scrolling',
29
+ apply_active_class_name_to: this.container,
30
+ notification_timeout_length: 125,
31
+ handle_minimum_length: 25,
32
+ scroll_to_smoothing: 0.01,
33
+ scroll_to_steps: 15,
34
+ scroll_to_precision: 100,
35
+ proportional: true,
36
+ custom_event: null,
37
+ custom_event_handler: null,
38
+ scroll_axis: 'vertical',
39
+ /**
40
+ * If this value is larger than 0 (e.g. 50), one mouse wheel event will scroll the page
41
+ * by the given number of pixels (default behaviour in any program). If it is lower than 0,
42
+ * the scrollbar's default behaviour will be used (scrolls down 1/20 of the page, no matter
43
+ * how long it is). Initialization with e.g.:
44
+ * new Control.ScrollBar(
45
+ document.getElementById('scrollbar_content'),
46
+ document.getElementById('scrollbar_track'),
47
+ { fixed_scroll_distance: 50 });
48
+ */
49
+ fixed_scroll_distance: -1,
50
+ slider_options: {}
51
+ },options || {});
52
+ this.slider = new Control.Slider(this.handle,this.track,Object.extend({
53
+ axis: this.options.scroll_axis,
54
+ onSlide: this.onChange.bind(this),
55
+ onChange: this.onChange.bind(this)
56
+ },this.options.slider_options));
57
+ this.recalculateLayout();
58
+ Event.observe(window,'resize',this.boundResizeObserver);
59
+ if (this.options.custom_event) {
60
+ if (Object.isFunction(this.options.custom_event_handler)) {
61
+ this.container.observe(this.options.custom_event, this.options.custom_event_handler);
62
+ } else {
63
+ this.container.observe(this.options.custom_event, this.boundResizeObserver);
64
+ }
65
+ }
66
+ this.handle.observe('mousedown',function(){
67
+ if(this.auto_sliding_executer)
68
+ this.auto_sliding_executer.stop();
69
+ }.bind(this));
70
+ },
71
+ destroy: function(){
72
+ Event.stopObserving(window,'resize',this.boundResizeObserver);
73
+ if(this.options.active_class_name)
74
+ $(this.options.apply_active_class_name_to).removeClassName(this.options.active_class_name);
75
+ if (this.options.custom_event) {
76
+ this.container.stopObserving(this.options.custom_event);
77
+ }
78
+ },
79
+ scrollLength: function(){
80
+ return (this.options.scroll_axis == 'vertical') ? this.container.scrollHeight : this.container.scrollWidth;
81
+ },
82
+ offsetLength: function(){
83
+ return (this.options.scroll_axis == 'vertical') ? this.container.offsetHeight : this.container.offsetWidth;
84
+ },
85
+ enable: function(){
86
+ this.enabled = true;
87
+ this.container.observe('mouse:wheel',this.boundMouseWheelEvent);
88
+ this.slider.setEnabled();
89
+ this.track.show();
90
+ if(this.options.active_class_name)
91
+ $(this.options.apply_active_class_name_to).addClassName(this.options.active_class_name);
92
+ this.notify('enabled');
93
+ },
94
+ disable: function(){
95
+ this.enabled = false;
96
+ this.container.stopObserving('mouse:wheel',this.boundMouseWheelEvent);
97
+ this.slider.setDisabled();
98
+ this.track.hide();
99
+ if(this.options.active_class_name)
100
+ $(this.options.apply_active_class_name_to).removeClassName(this.options.active_class_name);
101
+ this.notify('disabled');
102
+ this.reset();
103
+ },
104
+ reset: function(){
105
+ this.slider.setValue(0);
106
+ },
107
+ recalculateLayout: function(){
108
+ if(this.scrollLength() <= this.offsetLength())
109
+ this.disable();
110
+ else{
111
+ this.enable();
112
+ this.slider.trackLength = this.slider.maximumOffset() - this.slider.minimumOffset();
113
+ if(this.options.proportional){
114
+ this.slider.handleLength = Math.max(this.offsetLength() * (this.offsetLength() / this.scrollLength()),this.options.handle_minimum_length);
115
+ if (this.options.scroll_axis == 'vertical')
116
+ this.handle.style.height = this.slider.handleLength + 'px';
117
+ else
118
+ this.handle.style.width = this.slider.handleLength + 'px';
119
+ }
120
+ this.scrollBy(0);
121
+ }
122
+ },
123
+ onWindowResize: function(){
124
+ this.recalculateLayout();
125
+ this.scrollBy(0);
126
+ },
127
+ onMouseWheel: function(event){
128
+ if(this.auto_sliding_executer) {
129
+ this.auto_sliding_executer.stop();
130
+ }
131
+ if (this.options.fixed_scroll_distance > 0) {
132
+ // Move the content by the given number of pixels each wheel event
133
+ this.slider.setValueBy(-(this.options.fixed_scroll_distance * event.memo.delta / (this.scrollLength()-this.slider.trackLength)));
134
+ } else {
135
+ // Move the content by 1/20 of the page
136
+ this.slider.setValueBy(-(event.memo.delta / 20));
137
+ }
138
+ event.stop();
139
+ return false;
140
+ },
141
+ onChange: function(value){
142
+ var scroll_pos = Math.round(value / this.slider.maximum * (this.scrollLength() - this.offsetLength()));
143
+ if (this.options.scroll_axis == 'vertical')
144
+ this.container.scrollTop = scroll_pos;
145
+ else
146
+ this.container.scrollLeft = scroll_pos;
147
+ if(this.notification_timeout)
148
+ window.clearTimeout(this.notificationTimeout);
149
+ this.notificationTimeout = window.setTimeout(function(){
150
+ this.notify('change',value);
151
+ }.bind(this),this.options.notification_timeout_length);
152
+ },
153
+ getCurrentMaximumDelta: function(){
154
+ return this.slider.maximum * (this.scrollLength() - this.offsetLength());
155
+ },
156
+ getContainerOffset: function(element) {
157
+ var offset = element.positionedOffset();
158
+ while (element.getOffsetParent() != this.container)
159
+ {
160
+ element = element.getOffsetParent();
161
+ offset[0] += element.positionedOffset()[0];
162
+ offset[1] += element.positionedOffset()[1];
163
+ offset.top += element.positionedOffset().top;
164
+ offset.left += element.positionedOffset().left;
165
+ }
166
+ return offset;
167
+ },
168
+ getDeltaToElement: function(element){
169
+
170
+ if (this.options.scroll_axis == 'vertical')
171
+ return this.slider.maximum * ((this.getContainerOffset(element).top + (element.getHeight() / 2)) - (this.container.getHeight() / 2));
172
+ else
173
+ return this.slider.maximum * ((this.getContainerOffset(element).left + (element.getWidth() / 2)) - (this.container.getWidth() / 2));
174
+ },
175
+ scrollTo: function(y,animate){
176
+ var precision = this.options.scroll_to_precision,
177
+ current_maximum_delta = this.getCurrentMaximumDelta();
178
+ if (precision == 'auto')
179
+ precision = Math.pow(10, Math.ceil(Math.log(current_maximum_delta)/Math.log(10)));
180
+ if(y == 'top')
181
+ y = 0;
182
+ else if(y == 'bottom')
183
+ y = current_maximum_delta;
184
+ else if(typeof(y) != "number")
185
+ y = this.getDeltaToElement($(y));
186
+ if(this.enabled){
187
+ y = Math.max(0,Math.min(y,current_maximum_delta));
188
+ if(this.auto_sliding_executer)
189
+ this.auto_sliding_executer.stop();
190
+ var target_value = y / current_maximum_delta;
191
+ var original_slider_value = this.slider.value;
192
+ var delta = (target_value - original_slider_value) * current_maximum_delta;
193
+ if(animate){
194
+ this.auto_sliding_executer = new PeriodicalExecuter(function(){
195
+ if(Math.round(this.slider.value * precision) / precision < Math.round(target_value * precision) / precision || Math.round(this.slider.value * precision) / precision > Math.round(target_value * precision) / precision){
196
+ this.scrollBy(delta / this.options.scroll_to_steps);
197
+ }else{
198
+ this.auto_sliding_executer.stop();
199
+ this.auto_sliding_executer = null;
200
+ if(typeof(animate) == "function")
201
+ animate();
202
+ }
203
+ }.bind(this),this.options.scroll_to_smoothing);
204
+ }else
205
+ this.scrollBy(delta);
206
+ }else if(typeof(animate) == "function")
207
+ animate();
208
+ },
209
+ scrollBy: function(y){
210
+ if(!this.enabled)
211
+ return false;
212
+ this.slider.setValueBy(y / (this.getCurrentMaximumDelta() == 0 ? 1 : this.getCurrentMaximumDelta()) );
213
+ }
214
+ });
215
+ Object.extend(Control.ScrollBar,
216
+ {
217
+ instances: [],
218
+
219
+ findByElementId: function(id)
220
+ {
221
+ return Control.ScrollBar.instances.find(function(instance)
222
+ {
223
+ return (instance.container.id && instance.container.id == id);
224
+ });
225
+ }
226
+ });
227
+ Object.Event.extend(Control.ScrollBar);
js/livepipe/selection.js ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/selection
7
+ * @require prototype.js, effects.js, draggable.js, livepipe.js
8
+ */
9
+
10
+ /*global window, document, Prototype, Element, Event, $, $$, $break, Control, Draggable */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "Control.Selection requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "Control.Selection requires Object.Event to be loaded."; }
16
+
17
+ Control.Selection = {
18
+ options: {
19
+ resize_layout_timeout: 125,
20
+ selected: Prototype.emptyFunction,
21
+ deselected: Prototype.emptyFunction,
22
+ change: Prototype.emptyFunction,
23
+ selection_id: 'control_selection',
24
+ selection_style: {
25
+ zIndex: 999,
26
+ cursor: 'default',
27
+ border: '1px dotted #000'
28
+ },
29
+ filter: function(element){
30
+ return true;
31
+ },
32
+ drag_proxy: false,
33
+ drag_proxy_threshold: 1,
34
+ drag_proxy_options: {}
35
+ },
36
+ selectableElements: [],
37
+ elements: [],
38
+ selectableObjects: [],
39
+ objects: [],
40
+ active: false,
41
+ container: false,
42
+ resizeTimeout: false,
43
+ load: function(options){
44
+ Control.Selection.options = Object.extend(Control.Selection.options,options || {});
45
+ Control.Selection.selection_div = $(document.createElement('div'));
46
+ Control.Selection.selection_div.id = Control.Selection.options.selection_id;
47
+ Control.Selection.selection_div.style.display = 'none';
48
+ Control.Selection.selection_div.setStyle(Control.Selection.options.selection_style);
49
+ Control.Selection.border_width = parseInt(Control.Selection.selection_div.getStyle('border-top-width'), 10) * 2;
50
+ Control.Selection.container = Prototype.Browser.IE ? window.container : window;
51
+ $(document.body).insert(Control.Selection.selection_div);
52
+ Control.Selection.enable();
53
+ if(Control.Selection.options.drag_proxy && typeof(Draggable) != 'undefined') {
54
+ Control.Selection.DragProxy.load(); }
55
+ Event.observe(window,'resize',function(){
56
+ if(Control.Selection.resizeTimeout) {
57
+ window.clearTimeout(Control.Selection.resizeTimeout); }
58
+ Control.Selection.resizeTimeout = window.setTimeout(Control.Selection.recalculateLayout,Control.Selection.options.resize_layout_timeout);
59
+ });
60
+ if(Prototype.Browser.IE){
61
+ var body = $$('body').first();
62
+ body.observe('mouseleave',Control.Selection.stop);
63
+ body.observe('mouseup',Control.Selection.stop);
64
+ }
65
+ },
66
+ enable: function(){
67
+ if(Prototype.Browser.IE){
68
+ document.onselectstart = function(){
69
+ return false;
70
+ };
71
+ }
72
+ Event.observe(Control.Selection.container,'mousedown',Control.Selection.start);
73
+ Event.observe(Control.Selection.container,'mouseup',Control.Selection.stop);
74
+ },
75
+ disable: function(){
76
+ if(Prototype.Browser.IE){
77
+ document.onselectstart = function(){
78
+ return true;
79
+ };
80
+ }
81
+ Event.stopObserving(Control.Selection.container,'mousedown',Control.Selection.start);
82
+ Event.stopObserving(Control.Selection.container,'mouseup',Control.Selection.stop);
83
+ },
84
+ recalculateLayout: function(){
85
+ Control.Selection.selectableElements.each(function(element){
86
+ var dimensions = element.getDimensions();
87
+ var offset = element.cumulativeOffset();
88
+ var scroll_offset = element.cumulativeScrollOffset();
89
+ if(!element._control_selection) {
90
+ element._control_selection = {}; }
91
+ element._control_selection.top = offset[1] - scroll_offset[1];
92
+ element._control_selection.left = offset[0] - scroll_offset[0];
93
+ element._control_selection.width = dimensions.width;
94
+ element._control_selection.height = dimensions.height;
95
+ });
96
+ },
97
+ addSelectable: function(element,object,activation_targets,activation_target_callback){
98
+ element = $(element);
99
+ if(activation_targets) {
100
+ activation_targets = activation_targets.each ? activation_targets : [activation_targets]; }
101
+ var dimensions = element.getDimensions();
102
+ var offset = Element.cumulativeOffset(element);
103
+ element._control_selection = {
104
+ activation_targets: activation_targets,
105
+ is_selected: false,
106
+ top: offset[1],
107
+ left: offset[0],
108
+ width: dimensions.width,
109
+ height: dimensions.height,
110
+ activationTargetMouseMove: function(){
111
+ Control.Selection.notify('activationTargetMouseMove',element);
112
+ if(activation_targets){
113
+ activation_targets.each(function(activation_target){
114
+ activation_target.stopObserving('mousemove',element._control_selection.activationTargetMouseMove);
115
+ });
116
+ }
117
+ Control.Selection.DragProxy.container.stopObserving('mousemove',element._control_selection.activationTargetMouseMove);
118
+ },
119
+ activationTargetMouseDown: function(event){
120
+ if(!Control.Selection.elements.include(element)) {
121
+ Control.Selection.select(element); }
122
+ Control.Selection.DragProxy.start(event);
123
+ Control.Selection.DragProxy.container.hide();
124
+ if(activation_targets){
125
+ activation_targets.each(function(activation_target){
126
+ activation_target.observe('mousemove',element._control_selection.activationTargetMouseMove);
127
+ });
128
+ }
129
+ Control.Selection.DragProxy.container.observe('mousemove',element._control_selection.activationTargetMouseMove);
130
+ },
131
+ activationTargetClick: function(){
132
+ Control.Selection.select(element);
133
+ if(typeof(activation_target_callback) == "function") {
134
+ activation_target_callback(); }
135
+ if(activation_targets){
136
+ activation_targets.each(function(activation_target){
137
+ activation_target.stopObserving('mousemove',element._control_selection.activationTargetMouseMove);
138
+ });
139
+ }
140
+ Control.Selection.DragProxy.container.stopObserving('mousemove',element._control_selection.activationTargetMouseMove);
141
+ }
142
+ };
143
+ element.onselectstart = function(){
144
+ return false;
145
+ };
146
+ element.unselectable = 'on';
147
+ element.style.MozUserSelect = 'none';
148
+ if(activation_targets){
149
+ activation_targets.each(function(activation_target){
150
+ activation_target.observe('mousedown',element._control_selection.activationTargetMouseDown);
151
+ activation_target.observe('click',element._control_selection.activationTargetClick);
152
+ });
153
+ }
154
+ Control.Selection.selectableElements.push(element);
155
+ Control.Selection.selectableObjects.push(object);
156
+ },
157
+ removeSelectable: function(element){
158
+ element = $(element);
159
+ if(element._control_selection.activation_targets){
160
+ element._control_selection.activation_targets.each(function(activation_target){
161
+ activation_target.stopObserving('mousedown',element._control_selection.activationTargetMouseDown);
162
+ });
163
+ element._control_selection.activation_targets.each(function(activation_target){
164
+ activation_target.stopObserving('click',element._control_selection.activationTargetClick);
165
+ });
166
+ }
167
+ element._control_selection = null;
168
+ element.onselectstart = function() {
169
+ return true;
170
+ };
171
+ element.unselectable = 'off';
172
+ element.style.MozUserSelect = '';
173
+ var position = 0;
174
+ Control.Selection.selectableElements.each(function(selectable_element,i){
175
+ if(selectable_element == element){
176
+ position = i;
177
+ throw $break;
178
+ }
179
+ });
180
+ Control.Selection.selectableElements = Control.Selection.selectableElements.without(element);
181
+ Control.Selection.selectableObjects = Control.Selection.selectableObjects.slice(0,position).concat(Control.Selection.selectableObjects.slice(position + 1));
182
+ },
183
+ select: function(selected_elements){
184
+ if(typeof(selected_elements) == "undefined" || !selected_elements) {
185
+ selected_elements = []; }
186
+ if(!selected_elements.each && !selected_elements._each) {
187
+ selected_elements = [selected_elements]; }
188
+ //comparing the arrays directly wouldn't equate to true in safari so we need to compare each item
189
+ var selected_items_have_changed = !(Control.Selection.elements.length == selected_elements.length && Control.Selection.elements.all(function(item,i){
190
+ return selected_elements[i] == item;
191
+ }));
192
+ if(!selected_items_have_changed) {
193
+ return; }
194
+ var selected_objects_indexed_by_element = {};
195
+ var selected_objects = selected_elements.collect(function(selected_element){
196
+ var selected_object = Control.Selection.selectableObjects[Control.Selection.selectableElements.indexOf(selected_element)];
197
+ selected_objects_indexed_by_element[selected_element] = selected_object;
198
+ return selected_object;
199
+ });
200
+ if(Control.Selection.elements.length === 0 && selected_elements.length !== 0){
201
+ selected_elements.each(function(element){
202
+ Control.Selection.notify('selected',element,selected_objects_indexed_by_element[element]);
203
+ });
204
+ }else{
205
+ Control.Selection.elements.each(function(element){
206
+ if(!selected_elements.include(element)){
207
+ Control.Selection.notify('deselected',element,selected_objects_indexed_by_element[element]);
208
+ }
209
+ });
210
+ selected_elements.each(function(element){
211
+ if(!Control.Selection.elements.include(element)){
212
+ Control.Selection.notify('selected',element,selected_objects_indexed_by_element[element]);
213
+ }
214
+ });
215
+ }
216
+ Control.Selection.elements = selected_elements;
217
+ Control.Selection.objects = selected_objects;
218
+ Control.Selection.notify('change',Control.Selection.elements,Control.Selection.objects);
219
+ },
220
+ deselect: function(){
221
+ if(Control.Selection.notify('deselect') === false) {
222
+ return false; }
223
+ Control.Selection.elements.each(function(element){
224
+ Control.Selection.notify('deselected',element,Control.Selection.selectableObjects[Control.Selection.selectableElements.indexOf(element)]);
225
+ });
226
+ Control.Selection.objects = [];
227
+ Control.Selection.elements = [];
228
+ Control.Selection.notify('change',Control.Selection.objects,Control.Selection.elements);
229
+ return true;
230
+ },
231
+ //private
232
+ start: function(event){
233
+ if(!event.isLeftClick() || Control.Selection.notify('start',event) === false) {
234
+ return false; }
235
+ if(!event.shiftKey && !event.altKey) {
236
+ Control.Selection.deselect(); }
237
+ Event.observe(Control.Selection.container,'mousemove',Control.Selection.onMouseMove);
238
+ Event.stop(event);
239
+ return false;
240
+ },
241
+ stop: function(){
242
+ Event.stopObserving(Control.Selection.container,'mousemove',Control.Selection.onMouseMove);
243
+ Control.Selection.active = false;
244
+ Control.Selection.selection_div.setStyle({
245
+ display: 'none',
246
+ top: null,
247
+ left: null,
248
+ width: null,
249
+ height: null
250
+ });
251
+ Control.Selection.start_mouse_coordinates = {};
252
+ Control.Selection.current_mouse_coordinates = {};
253
+ },
254
+ mouseCoordinatesFromEvent: function(event){
255
+ return {
256
+ x: Event.pointerX(event),
257
+ y: Event.pointerY(event)
258
+ };
259
+ },
260
+ onClick: function(event,element,source){
261
+ var selection = [];
262
+ if(event.shiftKey){
263
+ selection = Control.Selection.elements.clone();
264
+ if(!selection.include(element)) {
265
+ selection.push(element); }
266
+ }else if(event.altKey){
267
+ selection = Control.Selection.elements.clone();
268
+ if(selection.include(element)) {
269
+ selection = selection.without(element); }
270
+ }else{
271
+ selection = [element];
272
+ }
273
+ Control.Selection.select(selection);
274
+ if(source == 'click') {
275
+ Event.stop(event); }
276
+ },
277
+ onMouseMove: function(event){
278
+ if(!Control.Selection.active){
279
+ Control.Selection.active = true;
280
+ Control.Selection.start_mouse_coordinates = Control.Selection.mouseCoordinatesFromEvent(event);
281
+ }else{
282
+ Control.Selection.current_mouse_coordinates = Control.Selection.mouseCoordinatesFromEvent(event);
283
+ Control.Selection.drawSelectionDiv();
284
+ var current_selection = Control.Selection.selectableElements.findAll(function(element){
285
+ return Control.Selection.options.filter(element) && Control.Selection.elementWithinSelection(element);
286
+ });
287
+ if(event.shiftKey && !event.altKey){
288
+ Control.Selection.elements.each(function(element){
289
+ if(!current_selection.include(element)) {
290
+ current_selection.push(element); }
291
+ });
292
+ }else if(event.altKey && !event.shiftKey){
293
+ current_selection = Control.Selection.elements.findAll(function(element){
294
+ return !current_selection.include(element);
295
+ });
296
+ }
297
+ Control.Selection.select(current_selection);
298
+ }
299
+ },
300
+ drawSelectionDiv: function(){
301
+ if(Control.Selection.start_mouse_coordinates == Control.Selection.current_mouse_coordinates){
302
+ Control.Selection.selection_div.style.display = 'none';
303
+ }else{
304
+ Control.Selection.viewport = document.viewport.getDimensions();
305
+ Control.Selection.selection_div.style.position = 'absolute';
306
+ Control.Selection.current_direction = (Control.Selection.start_mouse_coordinates.y > Control.Selection.current_mouse_coordinates.y ? 'N' : 'S') + (Control.Selection.start_mouse_coordinates.x < Control.Selection.current_mouse_coordinates.x ? 'E' : 'W');
307
+ Control.Selection.selection_div.setStyle(Control.Selection['dimensionsFor' + Control.Selection.current_direction]());
308
+ Control.Selection.selection_div.style.display = 'block';
309
+ }
310
+ },
311
+ dimensionsForNW: function(){
312
+ return {
313
+ top: (Control.Selection.start_mouse_coordinates.y - (Control.Selection.start_mouse_coordinates.y - Control.Selection.current_mouse_coordinates.y)) + 'px',
314
+ left: (Control.Selection.start_mouse_coordinates.x - (Control.Selection.start_mouse_coordinates.x - Control.Selection.current_mouse_coordinates.x)) + 'px',
315
+ width: (Control.Selection.start_mouse_coordinates.x - Control.Selection.current_mouse_coordinates.x) + 'px',
316
+ height: (Control.Selection.start_mouse_coordinates.y - Control.Selection.current_mouse_coordinates.y) + 'px'
317
+ };
318
+ },
319
+ dimensionsForNE: function(){
320
+ return {
321
+ top: (Control.Selection.start_mouse_coordinates.y - (Control.Selection.start_mouse_coordinates.y - Control.Selection.current_mouse_coordinates.y)) + 'px',
322
+ left: Control.Selection.start_mouse_coordinates.x + 'px',
323
+ width: Math.min((Control.Selection.viewport.width - Control.Selection.start_mouse_coordinates.x) - Control.Selection.border_width,Control.Selection.current_mouse_coordinates.x - Control.Selection.start_mouse_coordinates.x) + 'px',
324
+ height: (Control.Selection.start_mouse_coordinates.y - Control.Selection.current_mouse_coordinates.y) + 'px'
325
+ };
326
+ },
327
+ dimensionsForSE: function(){
328
+ return {
329
+ top: Control.Selection.start_mouse_coordinates.y + 'px',
330
+ left: Control.Selection.start_mouse_coordinates.x + 'px',
331
+ width: Math.min((Control.Selection.viewport.width - Control.Selection.start_mouse_coordinates.x) - Control.Selection.border_width,Control.Selection.current_mouse_coordinates.x - Control.Selection.start_mouse_coordinates.x) + 'px',
332
+ height: Math.min((Control.Selection.viewport.height - Control.Selection.start_mouse_coordinates.y) - Control.Selection.border_width,Control.Selection.current_mouse_coordinates.y - Control.Selection.start_mouse_coordinates.y) + 'px'
333
+ };
334
+ },
335
+ dimensionsForSW: function(){
336
+ return {
337
+ top: Control.Selection.start_mouse_coordinates.y + 'px',
338
+ left: (Control.Selection.start_mouse_coordinates.x - (Control.Selection.start_mouse_coordinates.x - Control.Selection.current_mouse_coordinates.x)) + 'px',
339
+ width: (Control.Selection.start_mouse_coordinates.x - Control.Selection.current_mouse_coordinates.x) + 'px',
340
+ height: Math.min((Control.Selection.viewport.height - Control.Selection.start_mouse_coordinates.y) - Control.Selection.border_width,Control.Selection.current_mouse_coordinates.y - Control.Selection.start_mouse_coordinates.y) + 'px'
341
+ };
342
+ },
343
+ inBoundsForNW: function(element,selection){
344
+ return (
345
+ ((element.left > selection.left || element.right > selection.left) && selection.right > element.left) &&
346
+ ((element.top > selection.top || element.bottom > selection.top) && selection.bottom > element.top)
347
+ );
348
+ },
349
+ inBoundsForNE: function(element,selection){
350
+ return (
351
+ ((element.left < selection.right || element.left < selection.right) && selection.left < element.right) &&
352
+ ((element.top > selection.top || element.bottom > selection.top) && selection.bottom > element.top)
353
+ );
354
+ },
355
+ inBoundsForSE: function(element,selection){
356
+ return (
357
+ ((element.left < selection.right || element.left < selection.right) && selection.left < element.right) &&
358
+ ((element.bottom < selection.bottom || element.top < selection.bottom) && selection.top < element.bottom)
359
+ );
360
+ },
361
+ inBoundsForSW: function(element,selection){
362
+ return (
363
+ ((element.left > selection.left || element.right > selection.left) && selection.right > element.left) &&
364
+ ((element.bottom < selection.bottom || element.top < selection.bottom) && selection.top < element.bottom)
365
+ );
366
+ },
367
+ elementWithinSelection: function(element){
368
+ if(Control.Selection['inBoundsFor' + Control.Selection.current_direction]({
369
+ top: element._control_selection.top,
370
+ left: element._control_selection.left,
371
+ bottom: element._control_selection.top + element._control_selection.height,
372
+ right: element._control_selection.left + element._control_selection.width
373
+ },{
374
+ top: parseInt(Control.Selection.selection_div.style.top, 10),
375
+ left: parseInt(Control.Selection.selection_div.style.left, 10),
376
+ bottom: parseInt(Control.Selection.selection_div.style.top, 10) + parseInt(Control.Selection.selection_div.style.height, 10),
377
+ right: parseInt(Control.Selection.selection_div.style.left, 10) + parseInt(Control.Selection.selection_div.style.width, 10)
378
+ })){
379
+ element._control_selection.is_selected = true;
380
+ return true;
381
+ }else{
382
+ element._control_selection.is_selected = false;
383
+ return false;
384
+ }
385
+ },
386
+ DragProxy: {
387
+ active: false,
388
+ xorigin: 0,
389
+ yorigin: 0,
390
+ load: function(){
391
+ Control.Selection.DragProxy.container = $(document.createElement('div'));
392
+ Control.Selection.DragProxy.container.id = 'control_selection_drag_proxy';
393
+ Control.Selection.DragProxy.container.setStyle({
394
+ position: 'absolute',
395
+ top: '1px',
396
+ left: '1px',
397
+ zIndex: 99999
398
+ });
399
+ Control.Selection.DragProxy.container.hide();
400
+ document.body.appendChild(Control.Selection.DragProxy.container);
401
+ Control.Selection.observe('selected',Control.Selection.DragProxy.selected);
402
+ Control.Selection.observe('deselected',Control.Selection.DragProxy.deselected);
403
+ },
404
+ start: function(event){
405
+ if(event.isRightClick()){
406
+ Control.Selection.DragProxy.container.hide();
407
+ return;
408
+ }
409
+ if(Control.Selection.DragProxy.xorigin == Event.pointerX(event) && Control.Selection.DragProxy.yorigin == Event.pointerY(event)) {
410
+ return; }
411
+ Control.Selection.DragProxy.active = true;
412
+ Control.Selection.DragProxy.container.setStyle({
413
+ position: 'absolute',
414
+ top: Event.pointerY(event) + 'px',
415
+ left: Event.pointerX(event) + 'px'
416
+ });
417
+ Control.Selection.DragProxy.container.observe('mouseup',Control.Selection.DragProxy.onMouseUp);
418
+ Control.Selection.DragProxy.container.show();
419
+ Control.Selection.DragProxy.container._draggable = new Draggable(Control.Selection.DragProxy.container,Object.extend({
420
+ onEnd: Control.Selection.DragProxy.stop
421
+ },Control.Selection.options.drag_proxy_options));
422
+ Control.Selection.DragProxy.container._draggable.eventMouseDown(event);
423
+ Control.Selection.DragProxy.notify('start',Control.Selection.DragProxy.container,Control.Selection.elements);
424
+ },
425
+ stop: function(){
426
+ window.setTimeout(function(){
427
+ Control.Selection.DragProxy.active = false;
428
+ Control.Selection.DragProxy.container.hide();
429
+ if(Control.Selection.DragProxy.container._draggable){
430
+ Control.Selection.DragProxy.container._draggable.destroy();
431
+ Control.Selection.DragProxy.container._draggable = null;
432
+ }
433
+ Control.Selection.DragProxy.notify('stop');
434
+ },1);
435
+ },
436
+ onClick: function(event){
437
+ Control.Selection.DragProxy.xorigin = Event.pointerX(event);
438
+ Control.Selection.DragProxy.yorigin = Event.pointerY(event);
439
+ if(event.isRightClick()) {
440
+ Control.Selection.DragProxy.container.hide(); }
441
+ if(Control.Selection.elements.length >= Control.Selection.options.drag_proxy_threshold && !(event.shiftKey || event.altKey) && (Control.Selection.DragProxy.xorigin != Event.pointerX(event) || Control.Selection.DragProxy.yorigin != Event.pointerY(event))){
442
+ Control.Selection.DragProxy.start(event);
443
+ Event.stop(event);
444
+ }
445
+ },
446
+ onMouseUp: function(event){
447
+ Control.Selection.DragProxy.stop();
448
+ Control.Selection.DragProxy.container.stopObserving('mouseup',Control.Selection.DragProxy.onMouseUp);
449
+ },
450
+ selected: function(element){
451
+ element.observe('mousedown',Control.Selection.DragProxy.onClick);
452
+ },
453
+ deselected: function(element){
454
+ element.stopObserving('mousedown',Control.Selection.DragProxy.onClick);
455
+ }
456
+ }
457
+ };
458
+ Object.Event.extend(Control.Selection);
459
+ Object.Event.extend(Control.Selection.DragProxy);
js/livepipe/selectmultiple.js ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/rating
7
+ * @require prototype.js, livepipe.js
8
+ */
9
+
10
+ /*global Prototype, Class, Option, $, $A, Control, $break, */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "Control.SelectMultiple requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "Control.SelectMultiple requires Object.Event to be loaded."; }
16
+
17
+ Control.SelectMultiple = Class.create({
18
+ select: false,
19
+ container: false,
20
+ numberOfCheckedBoxes: 0,
21
+ checkboxes: [],
22
+ hasExtraOption: false,
23
+ initialize: function(select,container,options){
24
+ this.options = {
25
+ checkboxSelector: 'input[type=checkbox]',
26
+ nameSelector: 'span.name',
27
+ labelSeparator: ', ',
28
+ valueSeparator: ',',
29
+ afterChange: Prototype.emptyFunction,
30
+ overflowString: function(str){
31
+ return str.truncate();
32
+ },
33
+ overflowLength: 30
34
+ };
35
+ Object.extend(this.options,options || {});
36
+ this.select = $(select);
37
+ this.container = $(container);
38
+ this.checkboxes = (typeof(this.options.checkboxSelector) == 'function') ?
39
+ this.options.checkboxSelector.bind(this)() :
40
+ this.container.getElementsBySelector(this.options.checkboxSelector);
41
+ var value_was_set = false;
42
+ if(this.options.value){
43
+ value_was_set = true;
44
+ this.setValue(this.options.value);
45
+ delete this.options.value;
46
+ }
47
+ this.hasExtraOption = false;
48
+ this.checkboxes.each(function(checkbox){
49
+ checkbox.observe('click',this.checkboxOnClick.bind(this,checkbox));
50
+ }.bind(this));
51
+ this.select.observe('change',this.selectOnChange.bind(this));
52
+ this.countAndCheckCheckBoxes();
53
+ if(!value_was_set) {
54
+ this.scanCheckBoxes(); }
55
+ this.notify('afterChange',this.select.options[this.select.options.selectedIndex].value);
56
+ },
57
+ countAndCheckCheckBoxes: function(){
58
+ this.numberOfCheckedBoxes = this.checkboxes.inject(0,function(number,checkbox){
59
+ checkbox.checked = (this.select.options[this.select.options.selectedIndex].value == checkbox.value);
60
+ var value_string = this.select.options[this.select.options.selectedIndex].value;
61
+ var value_collection = $A(value_string.split ? value_string.split(this.options.valueSeparator) : value_string);
62
+ var should_check = value_collection.any(function(value) {
63
+ if (!should_check && checkbox.value == value) {
64
+ return true; }
65
+ }.bind(this));
66
+ checkbox.checked = should_check;
67
+ if(checkbox.checked) {
68
+ ++number; }
69
+ return number;
70
+ }.bind(this));
71
+ },
72
+ setValue: function(value_string){
73
+ this.numberOfCheckedBoxes = 0;
74
+ var value_collection = $A(value_string.split ? value_string.split(this.options.valueSeparator) : value_string);
75
+ this.checkboxes.each(function(checkbox){
76
+ checkbox.checked = false;
77
+ value_collection.each(function(value){
78
+ if(checkbox.value == value){
79
+ ++this.numberOfCheckedBoxes;
80
+ checkbox.checked = true;
81
+ }
82
+ }.bind(this));
83
+ }.bind(this));
84
+ this.scanCheckBoxes();
85
+ },
86
+ selectOnChange: function(){
87
+ this.removeExtraOption();
88
+ this.countAndCheckCheckBoxes();
89
+ this.notify('afterChange',this.select.options[this.select.options.selectedIndex].value);
90
+ },
91
+ checkboxOnClick: function(checkbox){
92
+ this.numberOfCheckedBoxes = this.checkboxes.findAll(function (c) {
93
+ return c.checked;
94
+ }).length;
95
+ this.scanCheckBoxes();
96
+ this.notify('afterChange', this.numberOfCheckedBoxes === 0 ? "" :
97
+ this.select.options[this.select.options.selectedIndex].value);
98
+ },
99
+ scanCheckBoxes: function(){
100
+ switch(this.numberOfCheckedBoxes){
101
+ case 1:
102
+ this.checkboxes.each(function(checkbox){
103
+ if(checkbox.checked){
104
+ $A(this.select.options).each(function(option,i){
105
+ if(option.value == checkbox.value){
106
+ this.select.options.selectedIndex = i;
107
+ throw $break;
108
+ }
109
+ }.bind(this));
110
+ throw $break;
111
+ }
112
+ }.bind(this));
113
+ break;
114
+ case 0:
115
+ this.removeExtraOption();
116
+ break;
117
+ default:
118
+ this.addExtraOption();
119
+ break;
120
+ }
121
+ },
122
+ getLabelForExtraOption: function(){
123
+ var label = (typeof(this.options.nameSelector) == 'function' ?
124
+ this.options.nameSelector.bind(this)() :
125
+ this.container.getElementsBySelector(this.options.nameSelector).inject([],function(labels,name_element,i){
126
+ if(this.checkboxes[i].checked) {
127
+ labels.push(name_element.innerHTML); }
128
+ return labels;
129
+ }.bind(this))
130
+ ).join(this.options.labelSeparator);
131
+ return (label.length >= this.options.overflowLength && this.options.overflowLength > 0) ?
132
+ (typeof(this.options.overflowString) == 'function' ? this.options.overflowString(label) : this.options.overflowString) :
133
+ label;
134
+ },
135
+ getValueForExtraOption: function(){
136
+ return this.checkboxes.inject([],function(values,checkbox){
137
+ if(checkbox.checked) {
138
+ values.push(checkbox.value); }
139
+ return values;
140
+ }).join(this.options.valueSeparator);
141
+ },
142
+ addExtraOption: function(){
143
+ this.removeExtraOption();
144
+ this.hasExtraOption = true;
145
+ this.select.options[this.select.options.length] = new Option(this.getLabelForExtraOption(),this.getValueForExtraOption());
146
+ this.select.options.selectedIndex = this.select.options.length - 1;
147
+ },
148
+ removeExtraOption: function(){
149
+ if(this.hasExtraOption){
150
+ this.select.remove(this.select.options.length - 1);
151
+ this.hasExtraOption = false;
152
+ }
153
+ }
154
+ });
155
+ Object.Event.extend(Control.SelectMultiple);
js/livepipe/tabs.js ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/tabs
7
+ * @require prototype.js, livepipe.js
8
+ */
9
+
10
+ /*global window, document, Prototype, $, $A, $H, $break, Class, Element, Event, Control */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "Control.Tabs requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "Control.Tabs requires Object.Event to be loaded."; }
16
+
17
+ Control.Tabs = Class.create({
18
+ initialize: function(tab_list_container,options){
19
+ if(!$(tab_list_container)) {
20
+ throw "Control.Tabs could not find the element: " + tab_list_container; }
21
+ this.activeContainer = false;
22
+ this.activeLink = false;
23
+ this.containers = $H({});
24
+ this.links = [];
25
+ this.options = {
26
+ beforeChange: Prototype.emptyFunction,
27
+ afterChange: Prototype.emptyFunction,
28
+ hover: false,
29
+ tracked: true,
30
+ linkSelector: 'li a',
31
+ linkAttribute: 'href',
32
+ setClassOnContainer: false,
33
+ activeClassName: 'active',
34
+ disabledClassName: 'disabled',
35
+ defaultTab: 'first',
36
+ autoLinkExternal: true,
37
+ targetRegExp: /#(.+)$/,
38
+ showFunction: Element.show,
39
+ hideFunction: Element.hide
40
+ };
41
+ Object.extend(this.options,options || {});
42
+ if (this.options.tracked) {
43
+ Control.Tabs.instances.push(this);
44
+ }
45
+ var filterLinks;
46
+ switch (this.options.linkAttribute) {
47
+ case 'href':
48
+ case 'src':
49
+ filterLinks = function(link){
50
+ return (/^#/).test(link.getAttribute(this.options.linkAttribute).replace(
51
+ window.location.href.split('#')[0],''));
52
+ };
53
+ break;
54
+
55
+ default:
56
+ if (typeof(this.options.linkAttribute) == 'function') {
57
+ filterLinks = this.options.linkAttribute;
58
+ }
59
+ else {
60
+ filterLinks = function(link) {
61
+ return link.hasAttribute(this.options.linkAttribute);
62
+ };
63
+ }
64
+ }
65
+
66
+ (typeof(this.options.linkSelector) == 'string' ?
67
+ $(tab_list_container).select(this.options.linkSelector) :
68
+ this.options.linkSelector($(tab_list_container))
69
+ ).findAll(filterLinks.bind(this)).each(function(link){
70
+ this.addTab(link);
71
+ }.bind(this));
72
+ this.containers.values().each(Element.hide);
73
+ if(this.options.defaultTab == 'first') {
74
+ this.setActiveTab(this.links.first());
75
+ } else if(this.options.defaultTab == 'last') {
76
+ this.setActiveTab(this.links.last());
77
+ } else {
78
+ this.setActiveTab(this.options.defaultTab); }
79
+ var targets = this.options.targetRegExp.exec(window.location);
80
+ if(targets && targets[1]){
81
+ targets[1].split(',').each(function(target){
82
+ this.setActiveTab(this.links.find(function(link){
83
+ return link.key == target;
84
+ }));
85
+ }.bind(this));
86
+ }
87
+ if(this.options.autoLinkExternal){
88
+ $A(document.getElementsByTagName('a')).each(function(a){
89
+ if(!this.links.include(a)){
90
+ var clean_href = a.href.replace(window.location.href.split('#')[0],'');
91
+ if(clean_href.substring(0,1) == '#'){
92
+ if(this.containers.keys().include(clean_href.substring(1))){
93
+ $(a).observe('click',function(event,clean_href){
94
+ this.setActiveTab(clean_href.substring(1));
95
+ }.bindAsEventListener(this,clean_href));
96
+ }
97
+ }
98
+ }
99
+ }.bind(this));
100
+ }
101
+ },
102
+ addTab: function(link){
103
+ this.links.push(link);
104
+
105
+ switch (this.options.linkAttribute) {
106
+ case 'href':
107
+ case 'src':
108
+ link.key = link.getAttribute(this.options.linkAttribute).replace(
109
+ window.location.href.split('#')[0],'').split('#').last().replace(/#/,'');
110
+ break;
111
+
112
+ default:
113
+ if (typeof(this.options.linkAttribute) == 'function') {
114
+ link.key = this.options.linkAttribute(link);
115
+ }
116
+ else {
117
+ link.key = link.getAttribute(this.options.linkAttribute);
118
+ }
119
+ }
120
+ var container = this.options.tabs_container ? this.options.tabs_container.down('#'+link.key) : $(link.key);
121
+ if(!container) {
122
+ throw "Control.Tabs: #" + link.key + " was not found on the page."; }
123
+ this.containers.set(link.key,container);
124
+ link[this.options.hover ? 'onmouseover' : 'onclick'] = function(link){
125
+ if(window.event) {
126
+ Event.stop(window.event); }
127
+ this.setActiveTab(link);
128
+ return false;
129
+ }.bind(this,link);
130
+ },
131
+ getTab: function (link) {
132
+ if(!link && typeof(link) == 'undefined') {
133
+ return null; }
134
+ if(typeof(link) == 'string'){
135
+ return this.getTab(this.links.find(function(_link){
136
+ return _link.key == link;
137
+ }));
138
+ }else if(typeof(link) == 'number'){
139
+ return this.getTab(this.links[link]);
140
+ }else {
141
+ return this.containers.get(link.key);
142
+ }
143
+ },
144
+ setActiveTab: function(link){
145
+ if(!link && typeof(link) == 'undefined') {
146
+ return; }
147
+ if(typeof(link) == 'string'){
148
+ this.setActiveTab(this.links.find(function(_link){
149
+ return _link.key == link;
150
+ }));
151
+ }else if(typeof(link) == 'number'){
152
+ this.setActiveTab(this.links[link]);
153
+ }else if(!(this.options.setClassOnContainer ? $(link.parentNode) : link).hasClassName(this.options.disabledClassName)){
154
+ if(link == this.activeLink) {
155
+ return; }
156
+ if(this.notify('beforeChange',this.activeContainer,this.containers.get(link.key)) === false) {
157
+ return; }
158
+ if(this.activeContainer) {
159
+ this.options.hideFunction(this.activeContainer); }
160
+ this.links.each(function(item){
161
+ (this.options.setClassOnContainer ? $(item.parentNode) : item).removeClassName(this.options.activeClassName);
162
+ }.bind(this));
163
+ (this.options.setClassOnContainer ? $(link.parentNode) : link).addClassName(this.options.activeClassName);
164
+ this.activeContainer = this.containers.get(link.key);
165
+ this.activeLink = link;
166
+ this.options.showFunction(this.containers.get(link.key));
167
+ this.notify('afterChange',this.containers.get(link.key));
168
+ }
169
+ },
170
+ disableTab: function (link) {
171
+ if(!link && typeof(link) == 'undefined') {
172
+ return; }
173
+ if(typeof(link) == 'string'){
174
+ this.disableTab(this.links.find(function(_link){
175
+ return _link.key == link;
176
+ }));
177
+ }else if(typeof(link) == 'number'){
178
+ this.disableTab(this.links[link]);
179
+ }else{
180
+ if ({'INPUT':true,'BUTTON':true,'SELECT':true,'TEXTAREA':true}[link.nodeName]) {
181
+ link.disabled = true; }
182
+ (this.options.setClassOnContainer ? $(link.parentNode) : link).addClassName(this.options.disabledClassName);
183
+ }
184
+ },
185
+ enableTab: function (link) {
186
+ if(!link && typeof(link) == 'undefined') {
187
+ return; }
188
+ if(typeof(link) == 'string'){
189
+ this.enableTab(this.links.find(function(_link){
190
+ return _link.key == link;
191
+ }));
192
+ }else if(typeof(link) == 'number'){
193
+ this.enableTab(this.links[link]);
194
+ }else{
195
+ if ({'INPUT':true,'BUTTON':true,'SELECT':true,'TEXTAREA':true}[link.nodeName]) {
196
+ link.disabled = false; }
197
+ (this.options.setClassOnContainer ? $(link.parentNode) : link).removeClassName(this.options.disabledClassName);
198
+ }
199
+ },
200
+ next: function(){
201
+ this.links.each(function(link,i){
202
+ if(this.activeLink == link && this.links[i + 1]){
203
+ this.setActiveTab(this.links[i + 1]);
204
+ throw $break;
205
+ }
206
+ }.bind(this));
207
+ },
208
+ previous: function(){
209
+ this.links.each(function(link,i){
210
+ if(this.activeLink == link && this.links[i - 1]){
211
+ this.setActiveTab(this.links[i - 1]);
212
+ throw $break;
213
+ }
214
+ }.bind(this));
215
+ },
216
+ first: function(){
217
+ this.setActiveTab(this.links.first());
218
+ },
219
+ last: function(){
220
+ this.setActiveTab(this.links.last());
221
+ }
222
+ });
223
+ Object.extend(Control.Tabs,{
224
+ instances: [],
225
+ findByTabId: function(id){
226
+ return Control.Tabs.instances.find(function(tab){
227
+ return tab.links.find(function(link){
228
+ return link.key == id;
229
+ });
230
+ });
231
+ }
232
+ });
233
+ Object.Event.extend(Control.Tabs);
js/livepipe/textarea.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/textarea
7
+ * @require prototype.js, livepipe.js
8
+ */
9
+
10
+ /*global window, document, Prototype, Class, $, $A, Control */
11
+
12
+ if(typeof(Prototype) == "undefined") {
13
+ throw "Control.TextArea requires Prototype to be loaded."; }
14
+ if(typeof(Object.Event) == "undefined") {
15
+ throw "Control.TextArea requires Object.Event to be loaded."; }
16
+
17
+ Control.TextArea = Class.create({
18
+ initialize: function(textarea){
19
+ this.onChangeTimeout = false;
20
+ this.element = $(textarea);
21
+ $(this.element).observe('keyup',this.doOnChange.bindAsEventListener(this));
22
+ $(this.element).observe('paste',this.doOnChange.bindAsEventListener(this));
23
+ $(this.element).observe('input',this.doOnChange.bindAsEventListener(this));
24
+ if(!!document.selection){
25
+ $(this.element).observe('mouseup',this.saveRange.bindAsEventListener(this));
26
+ $(this.element).observe('keyup',this.saveRange.bindAsEventListener(this));
27
+ }
28
+ },
29
+ doOnChange: function(event){
30
+ if(this.onChangeTimeout) {
31
+ window.clearTimeout(this.onChangeTimeout); }
32
+ this.onChangeTimeout = window.setTimeout(function(){
33
+ this.notify('change',this.getValue());
34
+ }.bind(this),Control.TextArea.onChangeTimeoutLength);
35
+ },
36
+ saveRange: function(){
37
+ this.range = document.selection.createRange();
38
+ },
39
+ getValue: function(){
40
+ return this.element.value;
41
+ },
42
+ getSelection: function(){
43
+ if(!!document.selection) {
44
+ return document.selection.createRange().text; }
45
+ else if(!!this.element.setSelectionRange) {
46
+ return this.element.value.substring(this.element.selectionStart,this.element.selectionEnd); }
47
+ else {
48
+ return false; }
49
+ },
50
+ replaceSelection: function(text){
51
+ var scroll_top = this.element.scrollTop;
52
+ if(!!document.selection){
53
+ this.element.focus();
54
+ var range = (this.range) ? this.range : document.selection.createRange();
55
+ range.text = text;
56
+ range.select();
57
+ }else if(!!this.element.setSelectionRange){
58
+ var selection_start = this.element.selectionStart;
59
+ this.element.value = this.element.value.substring(0,selection_start) + text + this.element.value.substring(this.element.selectionEnd);
60
+ this.element.setSelectionRange(selection_start + text.length,selection_start + text.length);
61
+ }
62
+ this.doOnChange();
63
+ this.element.focus();
64
+ this.element.scrollTop = scroll_top;
65
+ },
66
+ wrapSelection: function(before,after){
67
+ var sel = this.getSelection();
68
+ // Remove the wrapping if the selection has the same before/after
69
+ if (sel.indexOf(before) === 0 &&
70
+ sel.lastIndexOf(after) === (sel.length - after.length)) {
71
+ this.replaceSelection(sel.substring(before.length,
72
+ sel.length - after.length));
73
+ } else { this.replaceSelection(before + sel + after); }
74
+ },
75
+ insertBeforeSelection: function(text){
76
+ this.replaceSelection(text + this.getSelection());
77
+ },
78
+ insertAfterSelection: function(text){
79
+ this.replaceSelection(this.getSelection() + text);
80
+ },
81
+ collectFromEachSelectedLine: function(callback,before,after){
82
+ this.replaceSelection((before || '') + $A(this.getSelection().split("\n")).collect(callback).join("\n") + (after || ''));
83
+ },
84
+ insertBeforeEachSelectedLine: function(text,before,after){
85
+ this.collectFromEachSelectedLine(function(line){
86
+ },before,after);
87
+ }
88
+ });
89
+ Object.extend(Control.TextArea,{
90
+ onChangeTimeoutLength: 500
91
+ });
92
+ Object.Event.extend(Control.TextArea);
93
+
94
+ Control.TextArea.ToolBar = Class.create( {
95
+ initialize: function(textarea,toolbar){
96
+ this.textarea = textarea;
97
+ if(toolbar) {
98
+ this.container = $(toolbar); }
99
+ else{
100
+ this.container = $(document.createElement('ul'));
101
+ this.textarea.element.parentNode.insertBefore(this.container,this.textarea.element);
102
+ }
103
+ },
104
+ attachButton: function(node,callback){
105
+ node.onclick = function(){return false;};
106
+ $(node).observe('click',callback.bindAsEventListener(this.textarea));
107
+ },
108
+ addButton: function(link_text,callback,attrs){
109
+ var li = document.createElement('li');
110
+ var a = document.createElement('a');
111
+ a.href = '#';
112
+ this.attachButton(a,callback);
113
+ li.appendChild(a);
114
+ Object.extend(a,attrs || {});
115
+ if(link_text){
116
+ var span = document.createElement('span');
117
+ span.innerHTML = link_text;
118
+ a.appendChild(span);
119
+ }
120
+ this.container.appendChild(li);
121
+ }
122
+ });
js/livepipe/window.js ADDED
@@ -0,0 +1,947 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @author Ryan Johnson <http://syntacticx.com/>
3
+ * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
4
+ * @package LivePipe UI
5
+ * @license MIT
6
+ * @url http://livepipe.net/control/window
7
+ * @require prototype.js, effects.js, draggable.js, resizable.js, livepipe.js
8
+ */
9
+
10
+ //adds onDraw and constrainToViewport option to draggable
11
+ if(typeof(Draggable) != 'undefined'){
12
+ //allows the point to be modified with an onDraw callback
13
+ Draggable.prototype.draw = function(point) {
14
+ var pos = Position.cumulativeOffset(this.element);
15
+ if(this.options.ghosting) {
16
+ var r = Position.realOffset(this.element);
17
+ pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
18
+ }
19
+
20
+ var d = this.currentDelta();
21
+ pos[0] -= d[0]; pos[1] -= d[1];
22
+
23
+ if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
24
+ pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
25
+ pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
26
+ }
27
+
28
+ var p = [0,1].map(function(i){
29
+ return (point[i]-pos[i]-this.offset[i])
30
+ }.bind(this));
31
+
32
+ if(this.options.snap) {
33
+ if(typeof this.options.snap == 'function') {
34
+ p = this.options.snap(p[0],p[1],this);
35
+ } else {
36
+ if(this.options.snap instanceof Array) {
37
+ p = p.map( function(v, i) {return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
38
+ } else {
39
+ p = p.map( function(v) {return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
40
+ }
41
+ }
42
+ }
43
+
44
+ if(this.options.onDraw)
45
+ this.options.onDraw.bind(this)(p);
46
+ else{
47
+ var style = this.element.style;
48
+ if(this.options.constrainToViewport){
49
+ var viewport_dimensions = document.viewport.getDimensions();
50
+ var container_dimensions = this.element.getDimensions();
51
+ var margin_top = parseInt(this.element.getStyle('margin-top'));
52
+ var margin_left = parseInt(this.element.getStyle('margin-left'));
53
+ var boundary = [[
54
+ 0 - margin_left,
55
+ 0 - margin_top
56
+ ],[
57
+ (viewport_dimensions.width - container_dimensions.width) - margin_left,
58
+ (viewport_dimensions.height - container_dimensions.height) - margin_top
59
+ ]];
60
+ if((!this.options.constraint) || (this.options.constraint=='horizontal')){
61
+ if((p[0] >= boundary[0][0]) && (p[0] <= boundary[1][0]))
62
+ this.element.style.left = p[0] + "px";
63
+ else
64
+ this.element.style.left = ((p[0] < boundary[0][0]) ? boundary[0][0] : boundary[1][0]) + "px";
65
+ }
66
+ if((!this.options.constraint) || (this.options.constraint=='vertical')){
67
+ if((p[1] >= boundary[0][1] ) && (p[1] <= boundary[1][1]))
68
+ this.element.style.top = p[1] + "px";
69
+ else
70
+ this.element.style.top = ((p[1] <= boundary[0][1]) ? boundary[0][1] : boundary[1][1]) + "px";
71
+ }
72
+ }else{
73
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
74
+ style.left = p[0] + "px";
75
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
76
+ style.top = p[1] + "px";
77
+ }
78
+ if(style.visibility=="hidden")
79
+ style.visibility = ""; // fix gecko rendering
80
+ }
81
+ };
82
+ }
83
+
84
+ if(typeof(Prototype) == "undefined")
85
+ throw "Control.Window requires Prototype to be loaded.";
86
+ if(typeof(IframeShim) == "undefined")
87
+ throw "Control.Window requires IframeShim to be loaded.";
88
+ if(typeof(Object.Event) == "undefined")
89
+ throw "Control.Window requires Object.Event to be loaded.";
90
+ /*
91
+ known issues:
92
+ - when iframe is clicked is does not gain focus
93
+ - safari can't open multiple iframes properly
94
+ - constrainToViewport: body must have no margin or padding for this to work properly
95
+ - iframe will be mis positioned during fade in
96
+ - document.viewport does not account for scrollbars (this will eventually be fixed in the prototype core)
97
+ notes
98
+ - setting constrainToViewport only works when the page is not scrollable
99
+ - setting draggable: true will negate the effects of position: center
100
+ */
101
+ Control.Window = Class.create({
102
+ initialize: function(container,options){
103
+ Control.Window.windows.push(this);
104
+
105
+ //attribute initialization
106
+ this.container = false;
107
+ this.isOpen = false;
108
+ this.href = false;
109
+ this.sourceContainer = false; //this is optionally the container that will open the window
110
+ this.ajaxRequest = false;
111
+ this.remoteContentLoaded = false; //this is set when the code to load the remote content is run, onRemoteContentLoaded is fired when the connection is closed
112
+ this.numberInSequence = Control.Window.windows.length + 1; //only useful for the effect scoping
113
+ this.indicator = false;
114
+ this.effects = {
115
+ fade: false,
116
+ appear: false
117
+ };
118
+ this.indicatorEffects = {
119
+ fade: false,
120
+ appear: false
121
+ };
122
+
123
+ //options
124
+ this.options = Object.extend({
125
+ //lifecycle
126
+ beforeOpen: Prototype.emptyFunction,
127
+ afterOpen: Prototype.emptyFunction,
128
+ beforeClose: Prototype.emptyFunction,
129
+ afterClose: Prototype.emptyFunction,
130
+ //dimensions and modes
131
+ height: null,
132
+ width: null,
133
+ className: false,
134
+ position: 'center', //'center', 'center_once', 'relative', [x,y], [function(){return x;},function(){return y;}]
135
+ offsetLeft: 0, //available only for anchors opening the window, or windows set to position: hover
136
+ offsetTop: 0, //""
137
+ iframe: false, //if the window has an href, this will display the href as an iframe instead of requesting the url as an an Ajax.Request
138
+ hover: false, //element object to hover over, or if "true" only available for windows with sourceContainer (an anchor or any element already on the page with an href attribute)
139
+ indicator: false, //element to show or hide when ajax requests, images and iframes are loading
140
+ closeOnClick: false, //does not work with hover,can be: true (click anywhere), 'container' (will refer to this.container), or element (a specific element)
141
+ iframeshim: true, //whether or not to position an iFrameShim underneath the window
142
+ //effects
143
+ fade: false,
144
+ fadeDuration: 0.75,
145
+ //draggable
146
+ draggable: false,
147
+ onDrag: Prototype.emptyFunction,
148
+ //resizable
149
+ resizable: false,
150
+ minHeight: false,
151
+ minWidth: false,
152
+ maxHeight: false,
153
+ maxWidth: false,
154
+ onResize: Prototype.emptyFunction,
155
+ //draggable and resizable
156
+ constrainToViewport: false,
157
+ //ajax
158
+ method: 'post',
159
+ parameters: {},
160
+ onComplete: Prototype.emptyFunction,
161
+ onSuccess: Prototype.emptyFunction,
162
+ onFailure: Prototype.emptyFunction,
163
+ onException: Prototype.emptyFunction,
164
+ //any element with an href (image,iframe,ajax) will call this after it is done loading
165
+ onRemoteContentLoaded: Prototype.emptyFunction,
166
+ insertRemoteContentAt: false //false will set this to this.container, can be string selector (first returned will be selected), or an Element that must be a child of this.container
167
+ },options || {});
168
+
169
+ //container setup
170
+ this.indicator = this.options.indicator ? $(this.options.indicator) : false;
171
+ if(container){
172
+ if(typeof(container) == "string" && container.match(Control.Window.uriRegex))
173
+ this.href = container;
174
+ else{
175
+ this.container = $(container);
176
+ //need to create the container now for tooltips (or hover: element with no container already on the page)
177
+ //second call made below will not create the container since the check is done inside createDefaultContainer()
178
+ this.createDefaultContainer(container);
179
+ //if an element with an href was passed in we use it to activate the window
180
+ if(this.container && ((this.container.readAttribute('href') && this.container.readAttribute('href') != '') || (this.options.hover && this.options.hover !== true))){
181
+ if(this.options.hover && this.options.hover !== true)
182
+ this.sourceContainer = $(this.options.hover);
183
+ else{
184
+ this.sourceContainer = this.container;
185
+ this.href = this.container.readAttribute('href');
186
+ var rel = this.href.match(/^#(.+)$/);
187
+ if(rel && rel[1]){
188
+ this.container = $(rel[1]);
189
+ this.href = false;
190
+ }else
191
+ this.container = false;
192
+ }
193
+ //hover or click handling
194
+ this.sourceContainerOpenHandler = function(event){
195
+ this.open(event);
196
+ event.stop();
197
+ return false;
198
+ }.bindAsEventListener(this);
199
+ this.sourceContainerCloseHandler = function(event){
200
+ this.close(event);
201
+ }.bindAsEventListener(this);
202
+ this.sourceContainerMouseMoveHandler = function(event){
203
+ this.position(event);
204
+ }.bindAsEventListener(this);
205
+ if(this.options.hover){
206
+ this.sourceContainer.observe('mouseenter',this.sourceContainerOpenHandler);
207
+ this.sourceContainer.observe('mouseleave',this.sourceContainerCloseHandler);
208
+ if(this.options.position == 'mouse')
209
+ this.sourceContainer.observe('mousemove',this.sourceContainerMouseMoveHandler);
210
+ }else
211
+ this.sourceContainer.observe('click',this.sourceContainerOpenHandler);
212
+ }
213
+ }
214
+ }
215
+ this.createDefaultContainer(container);
216
+ if(this.options.insertRemoteContentAt === false)
217
+ this.options.insertRemoteContentAt = this.container;
218
+ var styles = {
219
+ margin: 0,
220
+ position: 'absolute',
221
+ zIndex: Control.Window.initialZIndexForWindow()
222
+ };
223
+ if(this.options.width)
224
+ styles.width = $value(this.options.width) + 'px';
225
+ if(this.options.height)
226
+ styles.height = $value(this.options.height) + 'px';
227
+ this.container.setStyle(styles);
228
+ if(this.options.className)
229
+ this.container.addClassName(this.options.className);
230
+ this.positionHandler = this.position.bindAsEventListener(this);
231
+ this.outOfBoundsPositionHandler = this.ensureInBounds.bindAsEventListener(this);
232
+ this.bringToFrontHandler = this.bringToFront.bindAsEventListener(this);
233
+ this.container.observe('mousedown',this.bringToFrontHandler);
234
+ this.container.hide();
235
+ this.closeHandler = this.close.bindAsEventListener(this);
236
+ //iframeshim setup
237
+ if(this.options.iframeshim){
238
+ this.iFrameShim = new IframeShim();
239
+ this.iFrameShim.hide();
240
+ }
241
+ //resizable support
242
+ this.applyResizable();
243
+ //draggable support
244
+ this.applyDraggable();
245
+
246
+ //makes sure the window can't go out of bounds
247
+ Event.observe(window,'resize',this.outOfBoundsPositionHandler);
248
+
249
+ this.notify('afterInitialize');
250
+ },
251
+ open: function(event){
252
+ if(this.isOpen){
253
+ this.bringToFront();
254
+ return false;
255
+ }
256
+ if(this.notify('beforeOpen') === false)
257
+ return false;
258
+ //closeOnClick
259
+ if(this.options.closeOnClick){
260
+ if(this.options.closeOnClick === true)
261
+ this.closeOnClickContainer = $(document.body);
262
+ else if(this.options.closeOnClick == 'container')
263
+ this.closeOnClickContainer = this.container;
264
+ else if (this.options.closeOnClick == 'overlay'){
265
+ Control.Overlay.load();
266
+ this.closeOnClickContainer = Control.Overlay.container;
267
+ }else
268
+ this.closeOnClickContainer = $(this.options.closeOnClick);
269
+ this.closeOnClickContainer.observe('click',this.closeHandler);
270
+ }
271
+ if(this.href && !this.options.iframe && !this.remoteContentLoaded){
272
+ //link to image
273
+ this.remoteContentLoaded = true;
274
+ if(this.href.match(/\.(jpe?g|gif|png|tiff?)$/i)){
275
+ var img = new Element('img');
276
+ img.observe('load',function(img){
277
+ this.getRemoteContentInsertionTarget().insert(img);
278
+ this.position();
279
+ if(this.notify('onRemoteContentLoaded') !== false){
280
+ if(this.options.indicator)
281
+ this.hideIndicator();
282
+ this.finishOpen();
283
+ }
284
+ }.bind(this,img));
285
+ img.writeAttribute('src',this.href);
286
+ }else{
287
+ //if this is an ajax window it will only open if the request is successful
288
+ if(!this.ajaxRequest){
289
+ if(this.options.indicator)
290
+ this.showIndicator();
291
+ this.ajaxRequest = new Ajax.Request(this.href,{
292
+ method: this.options.method,
293
+ parameters: this.options.parameters,
294
+ onComplete: function(request){
295
+ this.notify('onComplete',request);
296
+ this.ajaxRequest = false;
297
+ }.bind(this),
298
+ onSuccess: function(request){
299
+ this.getRemoteContentInsertionTarget().insert(request.responseText);
300
+ this.notify('onSuccess',request);
301
+ if(this.notify('onRemoteContentLoaded') !== false){
302
+ if(this.options.indicator)
303
+ this.hideIndicator();
304
+ this.finishOpen();
305
+ }
306
+ }.bind(this),
307
+ onFailure: function(request){
308
+ this.notify('onFailure',request);
309
+ if(this.options.indicator)
310
+ this.hideIndicator();
311
+ }.bind(this),
312
+ onException: function(request,e){
313
+ this.notify('onException',request,e);
314
+ if(this.options.indicator)
315
+ this.hideIndicator();
316
+ }.bind(this)
317
+ });
318
+ }
319
+ }
320
+ return true;
321
+ }else if(this.options.iframe && !this.remoteContentLoaded){
322
+ //iframe
323
+ this.remoteContentLoaded = true;
324
+ if(this.options.indicator)
325
+ this.showIndicator();
326
+ this.getRemoteContentInsertionTarget().insert(Control.Window.iframeTemplate.evaluate({
327
+ href: this.href
328
+ }));
329
+ var iframe = this.container.down('iframe');
330
+ iframe.onload = function(){
331
+ this.notify('onRemoteContentLoaded');
332
+ if(this.options.indicator)
333
+ this.hideIndicator();
334
+ iframe.onload = null;
335
+ }.bind(this);
336
+ }
337
+ this.finishOpen(event);
338
+ return true
339
+ },
340
+ close: function(event){ //event may or may not be present
341
+ if(!this.isOpen || this.notify('beforeClose',event) === false)
342
+ return false;
343
+ if(this.options.closeOnClick)
344
+ this.closeOnClickContainer.stopObserving('click',this.closeHandler);
345
+ if(this.options.fade){
346
+ this.effects.fade = new Effect.Fade(this.container,{
347
+ queue: {
348
+ position: 'front',
349
+ scope: 'Control.Window' + this.numberInSequence
350
+ },
351
+ from: 1,
352
+ to: 0,
353
+ duration: this.options.fadeDuration / 2,
354
+ afterFinish: function(){
355
+ if(this.iFrameShim)
356
+ this.iFrameShim.hide();
357
+ this.isOpen = false;
358
+ this.notify('afterClose');
359
+ }.bind(this)
360
+ });
361
+ }else{
362
+ this.container.hide();
363
+ if(this.iFrameShim)
364
+ this.iFrameShim.hide();
365
+ }
366
+ if(this.ajaxRequest)
367
+ this.ajaxRequest.transport.abort();
368
+ if(!(this.options.draggable || this.options.resizable) && this.options.position == 'center')
369
+ Event.stopObserving(window,'resize',this.positionHandler);
370
+ if(!this.options.draggable && this.options.position == 'center')
371
+ Event.stopObserving(window,'scroll',this.positionHandler);
372
+ if(this.options.indicator)
373
+ this.hideIndicator();
374
+ if(!this.options.fade){
375
+ this.isOpen = false;
376
+ this.notify('afterClose');
377
+ }
378
+ return true;
379
+ },
380
+ position: function(event){
381
+ //this is up top for performance reasons
382
+ if(this.options.position == 'mouse'){
383
+ var xy = [Event.pointerX(event),Event.pointerY(event)];
384
+ this.container.setStyle({
385
+ top: xy[1] + $value(this.options.offsetTop) + 'px',
386
+ left: xy[0] + $value(this.options.offsetLeft) + 'px'
387
+ });
388
+ return;
389
+ }
390
+ var container_dimensions = this.container.getDimensions();
391
+ var viewport_dimensions = document.viewport.getDimensions();
392
+ Position.prepare();
393
+ var offset_left = (Position.deltaX + Math.floor((viewport_dimensions.width - container_dimensions.width) / 2));
394
+ var offset_top = (Position.deltaY + ((viewport_dimensions.height > container_dimensions.height) ? Math.floor((viewport_dimensions.height - container_dimensions.height) / 2) : 0));
395
+ if(this.options.position == 'center' || this.options.position == 'center_once'){
396
+ this.container.setStyle({
397
+ top: (container_dimensions.height <= viewport_dimensions.height) ? ((offset_top != null && offset_top > 0) ? offset_top : 0) + 'px' : 0,
398
+ left: (container_dimensions.width <= viewport_dimensions.width) ? ((offset_left != null && offset_left > 0) ? offset_left : 0) + 'px' : 0
399
+ });
400
+ }else if(this.options.position == 'relative'){
401
+ var xy = this.sourceContainer.cumulativeOffset();
402
+ var top = xy[1] + $value(this.options.offsetTop);
403
+ var left = xy[0] + $value(this.options.offsetLeft);
404
+ this.container.setStyle({
405
+ top: (container_dimensions.height <= viewport_dimensions.height) ? (this.options.constrainToViewport ? Math.max(0,Math.min(viewport_dimensions.height - (container_dimensions.height),top)) : top) + 'px' : 0,
406
+ left: (container_dimensions.width <= viewport_dimensions.width) ? (this.options.constrainToViewport ? Math.max(0,Math.min(viewport_dimensions.width - (container_dimensions.width),left)) : left) + 'px' : 0
407
+ });
408
+ }else if(this.options.position.length){
409
+ var top = $value(this.options.position[1]) + $value(this.options.offsetTop);
410
+ var left = $value(this.options.position[0]) + $value(this.options.offsetLeft);
411
+ this.container.setStyle({
412
+ top: (container_dimensions.height <= viewport_dimensions.height) ? (this.options.constrainToViewport ? Math.max(0,Math.min(viewport_dimensions.height - (container_dimensions.height),top)) : top) + 'px' : 0,
413
+ left: (container_dimensions.width <= viewport_dimensions.width) ? (this.options.constrainToViewport ? Math.max(0,Math.min(viewport_dimensions.width - (container_dimensions.width),left)) : left) + 'px' : 0
414
+ });
415
+ }
416
+ if(this.iFrameShim)
417
+ this.updateIFrameShimZIndex();
418
+ },
419
+ ensureInBounds: function(){
420
+ if(!this.isOpen)
421
+ return;
422
+ var viewport_dimensions = document.viewport.getDimensions();
423
+ var container_offset = this.container.cumulativeOffset();
424
+ var container_dimensions = this.container.getDimensions();
425
+ if(container_offset.left + container_dimensions.width > viewport_dimensions.width){
426
+ this.container.setStyle({
427
+ left: (Math.max(0,viewport_dimensions.width - container_dimensions.width)) + 'px'
428
+ });
429
+ }
430
+ if(container_offset.top + container_dimensions.height > viewport_dimensions.height){
431
+ this.container.setStyle({
432
+ top: (Math.max(0,viewport_dimensions.height - container_dimensions.height)) + 'px'
433
+ });
434
+ }
435
+ },
436
+ bringToFront: function(){
437
+ Control.Window.bringToFront(this);
438
+ this.notify('bringToFront');
439
+ },
440
+ destroy: function(){
441
+ this.container.stopObserving('mousedown',this.bringToFrontHandler);
442
+ if(this.draggable){
443
+ Draggables.removeObserver(this.container);
444
+ this.draggable.handle.stopObserving('mousedown',this.bringToFrontHandler);
445
+ this.draggable.destroy();
446
+ }
447
+ if(this.resizable){
448
+ Resizables.removeObserver(this.container);
449
+ this.resizable.handle.stopObserving('mousedown',this.bringToFrontHandler);
450
+ this.resizable.destroy();
451
+ }
452
+ if(this.container && !this.sourceContainer)
453
+ this.container.remove();
454
+ if(this.sourceContainer){
455
+ if(this.options.hover){
456
+ this.sourceContainer.stopObserving('mouseenter',this.sourceContainerOpenHandler);
457
+ this.sourceContainer.stopObserving('mouseleave',this.sourceContainerCloseHandler);
458
+ if(this.options.position == 'mouse')
459
+ this.sourceContainer.stopObserving('mousemove',this.sourceContainerMouseMoveHandler);
460
+ }else
461
+ this.sourceContainer.stopObserving('click',this.sourceContainerOpenHandler);
462
+ }
463
+ if(this.iFrameShim)
464
+ this.iFrameShim.destroy();
465
+ Event.stopObserving(window,'resize',this.outOfBoundsPositionHandler);
466
+ Control.Window.windows = Control.Window.windows.without(this);
467
+ this.notify('afterDestroy');
468
+ },
469
+ //private
470
+ applyResizable: function(){
471
+ if(this.options.resizable){
472
+ if(typeof(Resizable) == "undefined")
473
+ throw "Control.Window requires resizable.js to be loaded.";
474
+ var resizable_handle = null;
475
+ if(this.options.resizable === true){
476
+ resizable_handle = new Element('div',{
477
+ className: 'resizable_handle'
478
+ });
479
+ this.container.insert(resizable_handle);
480
+ }else
481
+ resizable_handle = $(this.options.resziable);
482
+ this.resizable = new Resizable(this.container,{
483
+ handle: resizable_handle,
484
+ minHeight: this.options.minHeight,
485
+ minWidth: this.options.minWidth,
486
+ maxHeight: this.options.constrainToViewport ? function(element){
487
+ //viewport height - top - total border height
488
+ return (document.viewport.getDimensions().height - parseInt(element.style.top || 0)) - (element.getHeight() - parseInt(element.style.height || 0));
489
+ } : this.options.maxHeight,
490
+ maxWidth: this.options.constrainToViewport ? function(element){
491
+ //viewport width - left - total border width
492
+ return (document.viewport.getDimensions().width - parseInt(element.style.left || 0)) - (element.getWidth() - parseInt(element.style.width || 0));
493
+ } : this.options.maxWidth
494
+ });
495
+ this.resizable.handle.observe('mousedown',this.bringToFrontHandler);
496
+ Resizables.addObserver(new Control.Window.LayoutUpdateObserver(this,function(){
497
+ if(this.iFrameShim)
498
+ this.updateIFrameShimZIndex();
499
+ this.notify('onResize');
500
+ }.bind(this)));
501
+ }
502
+ },
503
+ applyDraggable: function(){
504
+ if(this.options.draggable){
505
+ if(typeof(Draggables) == "undefined")
506
+ throw "Control.Window requires dragdrop.js to be loaded.";
507
+ var draggable_handle = null;
508
+ if(this.options.draggable === true){
509
+ draggable_handle = new Element('div',{
510
+ className: 'draggable_handle'
511
+ });
512
+ this.container.insert(draggable_handle);
513
+ }else
514
+ draggable_handle = $(this.options.draggable);
515
+ this.draggable = new Draggable(this.container,{
516
+ handle: draggable_handle,
517
+ constrainToViewport: this.options.constrainToViewport,
518
+ zindex: this.container.getStyle('z-index'),
519
+ starteffect: function(){
520
+ if(Prototype.Browser.IE){
521
+ this.old_onselectstart = document.onselectstart;
522
+ document.onselectstart = function(){
523
+ return false;
524
+ };
525
+ }
526
+ }.bind(this),
527
+ endeffect: function(){
528
+ document.onselectstart = this.old_onselectstart;
529
+ }.bind(this)
530
+ });
531
+ this.draggable.handle.observe('mousedown',this.bringToFrontHandler);
532
+ Draggables.addObserver(new Control.Window.LayoutUpdateObserver(this,function(){
533
+ if(this.iFrameShim)
534
+ this.updateIFrameShimZIndex();
535
+ this.notify('onDrag');
536
+ }.bind(this)));
537
+ }
538
+ },
539
+ createDefaultContainer: function(container){
540
+ if(!this.container){
541
+ //no container passed or found, create it
542
+ this.container = new Element('div',{
543
+ id: 'control_window_' + this.numberInSequence
544
+ });
545
+ $(document.body).insert(this.container);
546
+ if(typeof(container) == "string" && $(container) == null && !container.match(/^#(.+)$/) && !container.match(Control.Window.uriRegex))
547
+ this.container.update(container);
548
+ }
549
+ },
550
+ finishOpen: function(event){
551
+ this.bringToFront();
552
+ if(this.options.fade){
553
+ if(typeof(Effect) == "undefined")
554
+ throw "Control.Window requires effects.js to be loaded."
555
+ if(this.effects.fade)
556
+ this.effects.fade.cancel();
557
+ this.effects.appear = new Effect.Appear(this.container,{
558
+ queue: {
559
+ position: 'end',
560
+ scope: 'Control.Window.' + this.numberInSequence
561
+ },
562
+ from: 0,
563
+ to: 1,
564
+ duration: this.options.fadeDuration / 2,
565
+ afterFinish: function(){
566
+ if(this.iFrameShim)
567
+ this.updateIFrameShimZIndex();
568
+ this.isOpen = true;
569
+ this.notify('afterOpen');
570
+ }.bind(this)
571
+ });
572
+ }else
573
+ this.container.show();
574
+ this.position(event);
575
+ if(!(this.options.draggable || this.options.resizable) && this.options.position == 'center')
576
+ Event.observe(window,'resize',this.positionHandler,false);
577
+ if(!this.options.draggable && this.options.position == 'center')
578
+ Event.observe(window,'scroll',this.positionHandler,false);
579
+ if(!this.options.fade){
580
+ this.isOpen = true;
581
+ this.notify('afterOpen');
582
+ }
583
+ return true;
584
+ },
585
+ showIndicator: function(){
586
+ this.showIndicatorTimeout = window.setTimeout(function(){
587
+ if(this.options.fade){
588
+ this.indicatorEffects.appear = new Effect.Appear(this.indicator,{
589
+ queue: {
590
+ position: 'front',
591
+ scope: 'Control.Window.indicator.' + this.numberInSequence
592
+ },
593
+ from: 0,
594
+ to: 1,
595
+ duration: this.options.fadeDuration / 2
596
+ });
597
+ }else
598
+ this.indicator.show();
599
+ }.bind(this),Control.Window.indicatorTimeout);
600
+ },
601
+ hideIndicator: function(){
602
+ if(this.showIndicatorTimeout)
603
+ window.clearTimeout(this.showIndicatorTimeout);
604
+ this.indicator.hide();
605
+ },
606
+ getRemoteContentInsertionTarget: function(){
607
+ return typeof(this.options.insertRemoteContentAt) == "string" ? this.container.down(this.options.insertRemoteContentAt) : $(this.options.insertRemoteContentAt);
608
+ },
609
+ updateIFrameShimZIndex: function(){
610
+ if(this.iFrameShim)
611
+ this.iFrameShim.positionUnder(this.container);
612
+ }
613
+ });
614
+ //class methods
615
+ Object.extend(Control.Window,{
616
+ windows: [],
617
+ baseZIndex: 9999,
618
+ indicatorTimeout: 250,
619
+ iframeTemplate: new Template('<iframe src="#{href}" width="100%" height="100%" frameborder="0"></iframe>'),
620
+ uriRegex: /^(\/|\#|https?\:\/\/|[\w]+\/)/,
621
+ bringToFront: function(w){
622
+ Control.Window.windows = Control.Window.windows.without(w);
623
+ Control.Window.windows.push(w);
624
+ Control.Window.windows.each(function(w,i){
625
+ var z_index = Control.Window.baseZIndex + i;
626
+ w.container.setStyle({
627
+ zIndex: z_index
628
+ });
629
+ if(w.isOpen){
630
+ if(w.iFrameShim)
631
+ w.updateIFrameShimZIndex();
632
+ }
633
+ if(w.options.draggable)
634
+ w.draggable.options.zindex = z_index;
635
+ });
636
+ },
637
+ open: function(container,options){
638
+ var w = new Control.Window(container,options);
639
+ w.open();
640
+ return w;
641
+ },
642
+ //protected
643
+ initialZIndexForWindow: function(w){
644
+ return Control.Window.baseZIndex + (Control.Window.windows.length - 1);
645
+ }
646
+ });
647
+ Object.Event.extend(Control.Window);
648
+
649
+ //this is the observer for both Resizables and Draggables
650
+ Control.Window.LayoutUpdateObserver = Class.create({
651
+ initialize: function(w,observer){
652
+ this.w = w;
653
+ this.element = $(w.container);
654
+ this.observer = observer;
655
+ },
656
+ onStart: Prototype.emptyFunction,
657
+ onEnd: function(event_name,instance){
658
+ if(instance.element == this.element && this.iFrameShim)
659
+ this.w.updateIFrameShimZIndex();
660
+ },
661
+ onResize: function(event_name,instance){
662
+ if(instance.element == this.element)
663
+ this.observer(this.element);
664
+ },
665
+ onDrag: function(event_name,instance){
666
+ if(instance.element == this.element)
667
+ this.observer(this.element);
668
+ }
669
+ });
670
+
671
+ //overlay for Control.Modal
672
+ Control.Overlay = {
673
+ id: 'control_overlay',
674
+ loaded: false,
675
+ container: false,
676
+ lastOpacity: 0,
677
+ getStyles: function() {
678
+ return {
679
+ position: 'fixed',
680
+ top: 0,
681
+ left: 0,
682
+ width: '100%',
683
+ height: '100%',
684
+ zIndex: Control.Window.baseZIndex - 1
685
+ };
686
+ },
687
+ getIeStyles: function() {
688
+ return {
689
+ position: 'absolute',
690
+ top: 0,
691
+ left: 0,
692
+ zIndex: Control.Window.baseZIndex - 1
693
+ };
694
+ },
695
+ effects: {
696
+ fade: false,
697
+ appear: false
698
+ },
699
+ load: function(){
700
+ if(Control.Overlay.loaded)
701
+ return false;
702
+ Control.Overlay.loaded = true;
703
+ Control.Overlay.container = new Element('div',{
704
+ id: Control.Overlay.id
705
+ });
706
+ $(document.body).insert(Control.Overlay.container);
707
+ if(Prototype.Browser.IE){
708
+ Control.Overlay.container.setStyle(Control.Overlay.getIeStyles());
709
+ Event.observe(window,'scroll',Control.Overlay.positionOverlay);
710
+ Event.observe(window,'resize',Control.Overlay.positionOverlay);
711
+ Control.Overlay.observe('beforeShow',Control.Overlay.positionOverlay);
712
+ }else
713
+ Control.Overlay.container.setStyle(Control.Overlay.getStyles());
714
+ Control.Overlay.iFrameShim = new IframeShim();
715
+ Control.Overlay.iFrameShim.hide();
716
+ Event.observe(window,'resize',Control.Overlay.positionIFrameShim);
717
+ Control.Overlay.container.hide();
718
+ return true;
719
+ },
720
+ unload: function(){
721
+ if(!Control.Overlay.loaded)
722
+ return false;
723
+ Event.stopObserving(window,'resize',Control.Overlay.positionOverlay);
724
+ Control.Overlay.stopObserving('beforeShow',Control.Overlay.positionOverlay);
725
+ Event.stopObserving(window,'resize',Control.Overlay.positionIFrameShim);
726
+ Control.Overlay.iFrameShim.destroy();
727
+ Control.Overlay.container.remove();
728
+ Control.Overlay.loaded = false;
729
+ return true;
730
+ },
731
+ show: function(opacity,fade){
732
+ if(Control.Overlay.notify('beforeShow') === false)
733
+ return false;
734
+ Control.Overlay.lastOpacity = opacity;
735
+ Control.Overlay.positionIFrameShim();
736
+ Control.Overlay.iFrameShim.show();
737
+ if(fade){
738
+ if(typeof(Effect) == "undefined")
739
+ throw "Control.Window requires effects.js to be loaded."
740
+ if(Control.Overlay.effects.fade)
741
+ Control.Overlay.effects.fade.cancel();
742
+ Control.Overlay.effects.appear = new Effect.Appear(Control.Overlay.container,{
743
+ queue: {
744
+ position: 'end',
745
+ scope: 'Control.Overlay'
746
+ },
747
+ afterFinish: function(){
748
+ Control.Overlay.notify('afterShow');
749
+ },
750
+ from: 0,
751
+ to: Control.Overlay.lastOpacity,
752
+ duration: (fade === true ? 0.75 : fade) / 2
753
+ });
754
+ }else{
755
+ Control.Overlay.container.setStyle({
756
+ opacity: opacity || 1
757
+ });
758
+ Control.Overlay.container.show();
759
+ Control.Overlay.notify('afterShow');
760
+ }
761
+ return true;
762
+ },
763
+ hide: function(fade){
764
+ if(Control.Overlay.notify('beforeHide') === false)
765
+ return false;
766
+ if(Control.Overlay.effects.appear)
767
+ Control.Overlay.effects.appear.cancel();
768
+ Control.Overlay.iFrameShim.hide();
769
+ if(fade){
770
+ Control.Overlay.effects.fade = new Effect.Fade(Control.Overlay.container,{
771
+ queue: {
772
+ position: 'front',
773
+ scope: 'Control.Overlay'
774
+ },
775
+ afterFinish: function(){
776
+ Control.Overlay.notify('afterHide');
777
+ },
778
+ from: Control.Overlay.lastOpacity,
779
+ to: 0,
780
+ duration: (fade === true ? 0.75 : fade) / 2
781
+ });
782
+ }else{
783
+ Control.Overlay.container.hide();
784
+ Control.Overlay.notify('afterHide');
785
+ }
786
+ return true;
787
+ },
788
+ positionIFrameShim: function(){
789
+ if(Control.Overlay.container.visible())
790
+ Control.Overlay.iFrameShim.positionUnder(Control.Overlay.container);
791
+ },
792
+ //IE only
793
+ positionOverlay: function(){
794
+ Control.Overlay.container.setStyle({
795
+ width: document.body.clientWidth + 'px',
796
+ height: document.body.clientHeight + 'px'
797
+ });
798
+ }
799
+ };
800
+ Object.Event.extend(Control.Overlay);
801
+
802
+ Control.ToolTip = Class.create(Control.Window,{
803
+ initialize: function($super,container,tooltip,options){
804
+ $super(tooltip,Object.extend(Object.extend(Object.clone(Control.ToolTip.defaultOptions),options || {}),{
805
+ position: 'mouse',
806
+ hover: container
807
+ }));
808
+ }
809
+ });
810
+ Object.extend(Control.ToolTip,{
811
+ defaultOptions: {
812
+ offsetLeft: 10
813
+ }
814
+ });
815
+
816
+ Control.Modal = Class.create(Control.Window,{
817
+ initialize: function($super,container,options){
818
+ Control.Modal.InstanceMethods.beforeInitialize.bind(this)();
819
+ $super(container,Object.extend(Object.clone(Control.Modal.defaultOptions),options || {}));
820
+ },
821
+ closeWithoutOverlay: function(){
822
+ this.keepOverlay = true;
823
+ this.close();
824
+ }
825
+ });
826
+ Object.extend(Control.Modal,{
827
+ defaultOptions: {
828
+ overlayOpacity: 0.5,
829
+ closeOnClick: 'overlay'
830
+ },
831
+ current: false,
832
+ open: function(container,options){
833
+ var modal = new Control.Modal(container,options);
834
+ modal.open();
835
+ return modal;
836
+ },
837
+ close: function(){
838
+ if(Control.Modal.current)
839
+ Control.Modal.current.close();
840
+ },
841
+ InstanceMethods: {
842
+ beforeInitialize: function(){
843
+ Control.Overlay.load();
844
+ this.observe('beforeOpen',Control.Modal.Observers.beforeOpen.bind(this));
845
+ this.observe('afterOpen',Control.Modal.Observers.afterOpen.bind(this));
846
+ this.observe('afterClose',Control.Modal.Observers.afterClose.bind(this));
847
+ }
848
+ },
849
+ Observers: {
850
+ beforeOpen: function(){
851
+ Control.Window.windows.without(this).each(function(w){
852
+ if(w.closeWithoutOverlay && w.isOpen){
853
+ w.closeWithoutOverlay();
854
+ }else{
855
+ w.close();
856
+ }
857
+ });
858
+ if(!Control.Overlay.overlayFinishedOpening){
859
+ Control.Overlay.observeOnce('afterShow',function(){
860
+ Control.Overlay.overlayFinishedOpening = true;
861
+ this.open();
862
+ }.bind(this));
863
+ Control.Overlay.show(this.options.overlayOpacity,this.options.fade ? this.options.fadeDuration : false);
864
+ throw $break;
865
+ }
866
+ },
867
+ afterOpen: function(){
868
+ Control.Overlay.show(this.options.overlayOpacity);
869
+ Control.Overlay.overlayFinishedOpening = true;
870
+ Control.Modal.current = this;
871
+ },
872
+ afterClose: function(){
873
+ if(!this.keepOverlay){
874
+ Control.Overlay.hide(this.options.fade ? this.options.fadeDuration : false);
875
+ Control.Overlay.overlayFinishedOpening = false;
876
+ }
877
+ this.keepOverlay = false;
878
+ Control.Modal.current = false;
879
+ }
880
+ }
881
+ });
882
+
883
+ Control.LightBox = Class.create(Control.Window,{
884
+ initialize: function($super,container,options){
885
+ this.allImagesLoaded = false;
886
+ if(options.modal){
887
+ var options = Object.extend(Object.clone(Control.LightBox.defaultOptions),options || {});
888
+ options = Object.extend(Object.clone(Control.Modal.defaultOptions),options);
889
+ options = Control.Modal.InstanceMethods.beforeInitialize.bind(this)(options);
890
+ $super(container,options);
891
+ }else
892
+ $super(container,Object.extend(Object.clone(Control.LightBox.defaultOptions),options || {}));
893
+ this.hasRemoteContent = this.href && !this.options.iframe;
894
+ if(this.hasRemoteContent)
895
+ this.observe('onRemoteContentLoaded',Control.LightBox.Observers.onRemoteContentLoaded.bind(this));
896
+ else
897
+ this.applyImageObservers();
898
+ this.observe('beforeOpen',Control.LightBox.Observers.beforeOpen.bind(this));
899
+ },
900
+ applyImageObservers:function(){
901
+ var images = this.getImages();
902
+ this.numberImagesToLoad = images.length;
903
+ this.numberofImagesLoaded = 0;
904
+ images.each(function(image){
905
+ image.observe('load',function(image){
906
+ ++this.numberofImagesLoaded;
907
+ if(this.numberImagesToLoad == this.numberofImagesLoaded){
908
+ this.allImagesLoaded = true;
909
+ this.onAllImagesLoaded();
910
+ }
911
+ }.bind(this,image));
912
+ image.hide();
913
+ }.bind(this));
914
+ },
915
+ onAllImagesLoaded: function(){
916
+ this.getImages().each(function(image){
917
+ this.showImage(image);
918
+ }.bind(this));
919
+ if(this.hasRemoteContent){
920
+ if(this.options.indicator)
921
+ this.hideIndicator();
922
+ this.finishOpen();
923
+ }else
924
+ this.open();
925
+ },
926
+ getImages: function(){
927
+ return this.container.select(Control.LightBox.imageSelector);
928
+ },
929
+ showImage: function(image){
930
+ image.show();
931
+ }
932
+ });
933
+ Object.extend(Control.LightBox,{
934
+ imageSelector: 'img',
935
+ defaultOptions: {},
936
+ Observers: {
937
+ beforeOpen: function(){
938
+ if(!this.hasRemoteContent && !this.allImagesLoaded)
939
+ throw $break;
940
+ },
941
+ onRemoteContentLoaded: function(){
942
+ this.applyImageObservers();
943
+ if(!this.allImagesLoaded)
944
+ throw $break;
945
+ }
946
+ }
947
+ });
package.xml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Eabi_Livehandler</name>
4
+ <version>0.1.1</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/osl-3.0.php">OSL 3.0</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>&lt;p&gt;View detailed order information and navigate with up-down arrows in Order Grid&lt;/p&gt;</summary>
10
+ <description>&lt;p&gt;When in the Order Grid view, extra button is added next to Bill-to name&lt;/p&gt;&#xD;
11
+ &lt;p&gt;Clicking on the &lt;strong&gt;Show order&lt;/strong&gt; button opens up detailed order view in a modal box&lt;/p&gt;&#xD;
12
+ &lt;p&gt;You can navigate between orders by pressing directional keys or by pressing left/right icons on the order view&lt;/p&gt;&#xD;
13
+ &lt;p&gt;Possibility to create your own action buttons, which allows to save lots of time when managing orders&lt;/p&gt;&#xD;
14
+ &lt;p&gt;Possibility to purchase action buttons like &lt;strong&gt;Add order comment&lt;/strong&gt;, &lt;strong&gt;Create invoice&lt;/strong&gt;, &lt;strong&gt;Ship order&lt;/strong&gt; and more&lt;/p&gt;</description>
15
+ <notes>Initial release</notes>
16
+ <authors><author><name>Matis Matis</name><user>auto-converted</user><email>info@e-abi.ee</email></author></authors>
17
+ <date>2013-05-10</date>
18
+ <time>11:49:50</time>
19
+ <contents><target name="magecommunity"><dir name="Eabi"><dir name="Livehandler"><dir name="Block"><dir name="Adminhtml"><dir name="Config"><dir name="Form"><dir name="Field"><file name="Button.php" hash="33d3eb40f2c23ba4661956015336b8a4"/><file name="Remove.php" hash="54d09373da925081e36e90f0713660f2"/></dir></dir></dir></dir><file name="Footer.php" hash="23def3333fb7b464ef413d13d47b22bc"/></dir><dir name="Helper"><file name="Data.php" hash="210262e1f1929c97f03aa8ee87f30543"/></dir><dir name="Model"><dir name="Action"><file name="Abstract.php" hash="2cf8b22eca0a121c5ecd2bcd983e222d"/></dir><dir name="Adminhtml"><file name="Gridmanager.php" hash="c1af0869fe074a2c496545373e959907"/></dir><dir name="Mysql4"><dir name="Entry"><file name="Collection.php" hash="dc56ff6d43a19bf558366c0ac083ee25"/></dir><file name="Entry.php" hash="60f7ed0c56032c4edfa7f692d9d3c5f4"/></dir><dir name="System"><dir name="Config"><dir name="Backend"><file name="Button.php" hash="f6a5cf74036476d415e099c5cf7002ec"/></dir></dir></dir><file name="Abstract.php" hash="6cdd59c0287e6b4b37a34da627115c0f"/><file name="Entry.php" hash="cfda34f7cff6c58cee650722c04a0224"/><file name="Ordergrid.php" hash="b6df6c941135fba22d82e963349fa173"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="LivehandlerController.php" hash="6e106b3c9fa0117de3716ff3c9b47433"/><file name="RemoveController.php" hash="0901ae14e66230dabc3efb74065ff751"/></dir><file name="IndexController.php" hash="ab82c51048d2f5d36494252881809d52"/></dir><dir name="etc"><file name="config.xml" hash="092cd67dfa9fb3504f86d2b0124017f4"/><file name="system.xml" hash="937c6262391f79a5e21b0c6c32d6a3dd"/></dir><dir name="sql"><dir name="eabi_livehandler_setup"><file name="mysql4-install-0.1.0.php" hash="8f16385a82b16684ed7dc9e790a57ecf"/><file name="mysql4-upgrade-0.1.0-0.1.1.php" hash="d1fa18164c000376233ba200d42e729f"/></dir></dir><file name="CHANGELOG.txt" hash="c1462eeb9d109135e8d451c7341c4e3b"/><file name="LICENCE.txt" hash="0191312e121c0b3e1165619b96efcf9f"/></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="eabi_livehandler.xml" hash="662184a16683705946f3df92fea51a15"/></dir></dir></dir></dir><dir name="frontend"><dir name="default"><dir name="default"><dir name="layout"><file name="eabi_livehandler.xml" hash="205818bff253ab57a259c31c7cd29352"/></dir></dir></dir><dir name="base"><dir name="default"><dir name="layout"><file name="eabi_livehandler.xml" hash="205818bff253ab57a259c31c7cd29352"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Eabi_Livehandler.xml" hash="2579407bdedb1ea05cb5d516052d5503"/></dir></target><target name="magelocale"><dir name="en_US"><file name="Eabi_Livehandler.csv" hash="6e070637c1109df358ca7913a5a67458"/></dir><dir name="et_EE"><file name="Eabi_Livehandler.csv" hash="a30b4f480458b711e6a398e6fc153359"/></dir><dir name="fi_FI"><file name="Eabi_Livehandler.csv" hash="6e070637c1109df358ca7913a5a67458"/></dir><dir name="lt_LT"><file name="Eabi_Livehandler.csv" hash="6e070637c1109df358ca7913a5a67458"/></dir><dir name="sv_SE"><file name="Eabi_Livehandler.csv" hash="6e070637c1109df358ca7913a5a67458"/></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><file name="eabi_admintools.css" hash="fa4d08faa9c1300e9a543ff408557c38"/></dir></dir></dir></target><target name="mageweb"><dir name="js"><dir name="eabi_js"><file name="crossBrowser_initKeyboardEvent.js" hash="8600c5536225fd70c56ef313ee430e08"/></dir><dir name="livepipe"><file name="contextmenu.js" hash="a5dbab9663d94d9d848e84b524c1f925"/><file name="cookie.js" hash="eab2042ac637aec7777c7c64e3e2bb1a"/><file name="event_behavior.js" hash="a59a00ad652efb1596b5bd2e6f5cfe48"/><file name="hotkey.js" hash="c6193fc03e4cd4c94ee4fb37e3d5ed6b"/><file name="livepipe.js" hash="6e569402b2686976e7390f0b7a25b1eb"/><file name="progressbar.js" hash="69719eccbecbc0378d9919c53a3ef7dd"/><file name="rating.js" hash="adb5ce773c37b40fb590a877d949be7a"/><file name="resizable.js" hash="38fd18daa37e5612495cc7b42fd32b3d"/><file name="scrollbar.js" hash="7a2603c2107944b8a70e41e7a84133ba"/><file name="selection.js" hash="3f48981cccffdb5fcc5f5ff27d0b0b68"/><file name="selectmultiple.js" hash="d8f044eb344061bdcce405f671b15654"/><file name="tabs.js" hash="22abb9a3ec3933d54ff9ede0338ca7f3"/><file name="textarea.js" hash="9825782bf78d38efa53200a206d8cd77"/><file name="window.js" hash="f6eba488d6a80e05f59d97a0cef59730"/></dir></dir></target></contents>
20
+ <compatible/>
21
+ <dependencies/>
22
+ </package>
skin/adminhtml/default/default/eabi_admintools.css ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Document : eabi_admintools.css
3
+ Created on : 2.05.2012, 12:58:01
4
+ Author : matishalmann
5
+ Description:
6
+ Purpose of the stylesheet follows.
7
+ */
8
+
9
+ #control_overlay {
10
+ background:none repeat scroll 0 0 #000000;
11
+ opacity: 0.5;
12
+ filter: alpha(opacity=50);
13
+ filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
14
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
15
+ }
16
+
17
+ .modal {
18
+ background-color:#fff;
19
+ padding:10px;
20
+ border:1px solid #333;
21
+ }
22
+
23
+ .tooltip {
24
+ border:1px solid #000;
25
+ background-color:#fff;
26
+ height:25px;
27
+ width:200px;
28
+ font-family:"Lucida Grande",Verdana;
29
+ font-size:10px;
30
+ color:#333;
31
+ }
32
+
33
+ .simple_window {
34
+ width:250px;
35
+ height:50px;
36
+ border:1px solid #000;
37
+ background-color:#fff;
38
+ padding:10px;
39
+ text-align:left;
40
+ font-family:"Lucida Grande",Verdana;
41
+ font-size:12px;
42
+ color:#333;
43
+ }
44
+
45
+ .eabi_window {
46
+ background-image:url("images/bg_window_mask.png");
47
+ background-position:top left;
48
+ -moz-border-radius: 10px;
49
+ -webkit-border-radius: 10px;
50
+ border-radius: 10px;
51
+ padding:10px;
52
+ font-family:"Lucida Grande",Verdana;
53
+ font-size:13px;
54
+ color:#000;
55
+ text-align:center;
56
+ min-width:550px;
57
+ min-height:229px;
58
+ width: 768px;
59
+ }
60
+
61
+
62
+ .eabi_window .button_box {
63
+ border: 1px solid #000;
64
+ background: #fff;
65
+ height: 100px;
66
+ width: 300px;
67
+ padding: 5px;
68
+
69
+ }
70
+
71
+ .eabi_window_bg {
72
+ background-image:url("images/box_bg.gif");
73
+ }
74
+
75
+ .eabi_window .window_contents {
76
+ margin-top:10px;
77
+ width:100%;
78
+ height:100%;
79
+ }
80
+
81
+ .eabi_window .window_contents a {
82
+ display: inline !important;
83
+ }
84
+ .eabi_window .eabi_arrow {
85
+ height: 13px;
86
+ width: 13px;
87
+ cursor: pointer;
88
+ display: inline-block;
89
+ position: absolute;
90
+ top: 100px;
91
+ }
92
+ .eabi_window #eabi_previous {
93
+ float: left;
94
+ left: 0px;
95
+ background: url("images/pager_arrow_left.gif") no-repeat;
96
+ }
97
+ .eabi_window #eabi_next {
98
+ float: right;
99
+ right: 0px;
100
+ background: url("images/pager_arrow_right.gif") no-repeat;
101
+ }
102
+
103
+ .eabi_window .window_header {
104
+ text-align:center;
105
+ }
106
+
107
+ .eabi_window .window_title {
108
+ margin-top:-7px;
109
+ margin-bottom:7px;
110
+ font-size:13px;
111
+ font-weight: bold;
112
+ }
113
+
114
+ .eabi_window .window_close {
115
+ display:block;
116
+ position:absolute;
117
+ top:4px;
118
+ right:5px;
119
+ height:16px;
120
+ width:16px;
121
+ background-image: url("images/window_close.png");
122
+ cursor:pointer;
123
+ background-repeat: no-repeat;
124
+ }
125
+
126
+ .eabi_window .window_contents .products_grid tr {
127
+ width: 764px;
128
+ }
129
+ .eabi_window .window_contents .products_grid {
130
+ width: 768px;
131
+ }
132
+ .eabi_window .window_contents .products_grid th {
133
+ max-width: 190px;
134
+ }
135
+ .eabi_window .window_contents .products_grid td {
136
+ max-width: 190px;
137
+ }
138
+
139
+ .eabi_window_bg .window_close {
140
+ background-image:url("images/icon_btn_delete.gif");
141
+ }
142
+
143
+ .eabi_window .eabi_orderview_actionbutton {
144
+ float: left;
145
+ }
146
+
147
+ .eabi_window .eabi_buttons {
148
+ max-width: 455px;
149
+ display: table;
150
+ float: left;
151
+ }
152
+
153
+ .eabi_window .eabi_commercial {
154
+ color: #2d444f;
155
+ }
156
+ .eabi_window .eabi_commercial a {
157
+ text-decoration: none;
158
+ color: #ea7601 !important;
159
+ }
160
+ .eabi_window .eabi_commercial a:hover {
161
+ text-decoration: underline !important;
162
+ }
163
+
164
+ .eabi_window .order-totals table td {
165
+ padding: 0 !important;
166
+ }
167
+ .eabi_window .order-totals table td.label {
168
+ padding-right: 10px !important;
169
+ }