ServerLoadMonitorFree - Version 1.0.0

Version Notes

02.29.2013 - Extension release (Compatibility: CE 1.4.х-1.7.0.2)

Download this release

Release Info

Developer Aitoc
Extension ServerLoadMonitorFree
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

Files changed (65) hide show
  1. License agreement Server Load Monitor.pdf +0 -0
  2. Server Load Monitor Manual.pdf +0 -0
  3. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitlink.php +22 -0
  4. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitloadmon.php +60 -0
  5. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitloadmon/BarGraph.php +166 -0
  6. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitloadmon/Filter.php +150 -0
  7. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitloadmon/LineGraph.php +204 -0
  8. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Calculator.php +59 -0
  9. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/CalculatorResult.php +44 -0
  10. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Indicator.php +38 -0
  11. app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Manage.php +133 -0
  12. app/code/local/Aitoc/Aitloadmon/Helper/Data.php +67 -0
  13. app/code/local/Aitoc/Aitloadmon/Model/Aitloadmon.php +34 -0
  14. app/code/local/Aitoc/Aitloadmon/Model/Analytics.php +32 -0
  15. app/code/local/Aitoc/Aitloadmon/Model/Calculator.php +129 -0
  16. app/code/local/Aitoc/Aitloadmon/Model/Calculator/Abstract.php +90 -0
  17. app/code/local/Aitoc/Aitloadmon/Model/Calculator/Hardware.php +69 -0
  18. app/code/local/Aitoc/Aitloadmon/Model/Calculator/Magento.php +142 -0
  19. app/code/local/Aitoc/Aitloadmon/Model/Calculator/Software.php +104 -0
  20. app/code/local/Aitoc/Aitloadmon/Model/CompressSource.php +56 -0
  21. app/code/local/Aitoc/Aitloadmon/Model/Dblog.php +16 -0
  22. app/code/local/Aitoc/Aitloadmon/Model/LevelSource.php +66 -0
  23. app/code/local/Aitoc/Aitloadmon/Model/Manage.php +31 -0
  24. app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Aitloadmon.php +170 -0
  25. app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Aitloadmon/Collection.php +67 -0
  26. app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Calculator.php +25 -0
  27. app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Calculator/Collection.php +15 -0
  28. app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Dblog.php +17 -0
  29. app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Dblog/Collection.php +15 -0
  30. app/code/local/Aitoc/Aitloadmon/Model/Mysql4/UrlRewrite.php +64 -0
  31. app/code/local/Aitoc/Aitloadmon/Model/Observer.php +48 -0
  32. app/code/local/Aitoc/Aitloadmon/Model/Process.php +309 -0
  33. app/code/local/Aitoc/Aitloadmon/Model/Router.php +162 -0
  34. app/code/local/Aitoc/Aitloadmon/Model/SysInfo.php +12 -0
  35. app/code/local/Aitoc/Aitloadmon/Model/System.php +12 -0
  36. app/code/local/Aitoc/Aitloadmon/Model/System/Abstract.php +33 -0
  37. app/code/local/Aitoc/Aitloadmon/Model/UrlRewrite.php +25 -0
  38. app/code/local/Aitoc/Aitloadmon/controllers/Adminhtml/AitloadmonController.php +108 -0
  39. app/code/local/Aitoc/Aitloadmon/controllers/Adminhtml/CalculatorController.php +42 -0
  40. app/code/local/Aitoc/Aitloadmon/etc/adminhtml.xml +44 -0
  41. app/code/local/Aitoc/Aitloadmon/etc/config.xml +112 -0
  42. app/code/local/Aitoc/Aitloadmon/etc/system.xml +67 -0
  43. app/code/local/Aitoc/Aitloadmon/sql/aitloadmon_setup/mysql4-install-1.0.0.php +49 -0
  44. app/code/local/Aitoc/Aitloadmon/sql/aitloadmon_setup/mysql4-uninstall-1.0.0.php +21 -0
  45. app/code/local/Aitoc/Aitsys/install/Aitoc_Aitloadmon.phtml +112 -0
  46. app/design/adminhtml/default/default/layout/aitloadmon.xml +50 -0
  47. app/design/adminhtml/default/default/template/aitloadmon/back.phtml +8 -0
  48. app/design/adminhtml/default/default/template/aitloadmon/bar.phtml +30 -0
  49. app/design/adminhtml/default/default/template/aitloadmon/graph.phtml +28 -0
  50. app/design/adminhtml/default/default/template/aitloadmon/indicator.phtml +12 -0
  51. app/design/adminhtml/default/default/template/aitloadmon/line.phtml +115 -0
  52. app/design/adminhtml/default/default/template/aitloadmon/quick.phtml +28 -0
  53. app/design/adminhtml/default/default/template/aitloadmon/sysInfo.phtml +11 -0
  54. app/design/adminhtml/default/default/template/aitloadmon/system.phtml +11 -0
  55. app/etc/modules/Aitoc_Aitloadmon.xml +16 -0
  56. js/aitoc/aitloadmon/grafico.bar.custom.js +204 -0
  57. js/aitoc/aitloadmon/grafico.line.custom.js +138 -0
  58. js/aitoc/aitloadmon/grafico.min.js +80 -0
  59. js/aitoc/aitloadmon/raphael-min.js +7 -0
  60. lib/Aitoc/Aitloadmon/Adapter/Apc.php +56 -0
  61. lib/Aitoc/Aitloadmon/Adapter/File.php +116 -0
  62. lib/Aitoc/Aitloadmon/Adapter/Memcached.php +66 -0
  63. lib/Aitoc/Aitloadmon/Collect.php +266 -0
  64. package.xml +37 -0
  65. readme.txt +34 -0
License agreement Server Load Monitor.pdf ADDED
Binary file
Server Load Monitor Manual.pdf ADDED
Binary file
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitlink.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_Aitlink extends Mage_Adminhtml_Block_System_Config_Form_Field
7
+ {
8
+ protected function _getElementHtml (Varien_Data_Form_Element_Abstract $element)
9
+ {
10
+ $buttonBlock = $element->getForm()->getParent()->getLayout()->createBlock('adminhtml/widget_button');
11
+
12
+ $data = array(
13
+ 'label' => Mage::helper('adminhtml')->__('Manage'),
14
+ 'onclick' => 'setLocation(\''.Mage::helper('adminhtml')->getUrl("adminhtml/aitloadmon/manage").'\' )',
15
+ 'class' => '',
16
+ );
17
+
18
+ $html = $buttonBlock->setData($data)->toHtml();
19
+
20
+ return $html;
21
+ }
22
+ }
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitloadmon.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_Aitloadmon extends Mage_Adminhtml_Block_Template
7
+ {
8
+
9
+ protected $_collection;
10
+ protected $_compareCollection;
11
+
12
+ /**
13
+ * Returns a collection of data for main graphics
14
+ *
15
+ * @return Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon_Collection
16
+ */
17
+ public function getCollection()
18
+ {
19
+ if(!isset($this->_collection))
20
+ {
21
+ $this->_collection = Mage::getModel('aitloadmon/aitloadmon')->getCollectionByDates($this->getData('start_date'),$this->getData('end_date'));
22
+ }
23
+ return $this->_collection;
24
+ }
25
+
26
+ /**
27
+ * Returns a collection of data for compare graphics
28
+ *
29
+ * @return Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon_Collection
30
+ */
31
+ public function getCompareCollection()
32
+ {
33
+ if(!isset($this->_compareCollection))
34
+ {
35
+ $this->_compareCollection = Mage::getModel('aitloadmon/aitloadmon')->getCollectionByDates($this->getData('start_date_compare'),$this->getData('end_date_compare'));
36
+ }
37
+ return $this->_compareCollection;
38
+ }
39
+
40
+ /**
41
+ * Checks if there is enough data in collection to build graphics (more then 2 dots)
42
+ *
43
+ * @param Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon_Collection $collection
44
+ * @return bool
45
+ */
46
+ public function isEnoughDataToBuildGraph($collection)
47
+ {
48
+ $data = array();
49
+ foreach($collection as $item)
50
+ {
51
+ $data[$item->getMeasureTime()] = 1;
52
+ if(count($data)>2)
53
+ {
54
+ return true;
55
+ }
56
+ }
57
+ return false;
58
+ }
59
+
60
+ }
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitloadmon/BarGraph.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_Aitloadmon_BarGraph extends Mage_Adminhtml_Block_Widget_Form
7
+ {
8
+ /**
9
+ * Gets collection(s) from parent block and processes data for javascript graphics
10
+ */
11
+ protected function _beforeToHtml()
12
+ {
13
+ $collection = $this->getParentBlock()->getCollection();
14
+ if($this->getCompare())
15
+ {
16
+ $collectionCompare = $this->getParentBlock()->getCompareCollection();
17
+ }
18
+
19
+ $barData = $this->_preProcessDataForBar($collection);
20
+ $barDataCompare = null;
21
+ if($this->getCompare())
22
+ {
23
+ $barDataCompare = $this->_preProcessDataForBar($collectionCompare);
24
+ }
25
+ $this->_processDataForBar($barData,$barDataCompare);
26
+ }
27
+
28
+ /**
29
+ * Returns whether the compare mode is enabled
30
+ *
31
+ * @return bool
32
+ */
33
+ public function getCompare()
34
+ {
35
+ return $this->getParentBlock()->getCompare();
36
+ }
37
+
38
+ /**
39
+ * Pre-processes data for further data processing
40
+ *
41
+ * @param Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon_Collection $collection
42
+ * @param bool $dataForCompare [false]
43
+ * @return array
44
+ */
45
+ private function _preProcessDataForBar($collection, $dataForCompare = false)
46
+ {
47
+ $data = array();
48
+ $totalPageViews = 0;
49
+ $maxLoad = 0;
50
+ $maxLoadTime = 0;
51
+ $totalPageViewsByGroup = array();
52
+
53
+ foreach($collection as $item)
54
+ {
55
+ $pageGroupId = $item->getData('page_group_id');
56
+ if(!isset($totalPageViewsByGroup[$pageGroupId]))
57
+ {
58
+ $totalPageViewsByGroup[$pageGroupId]=0;
59
+ }
60
+ $totalPageViewsByGroup[$pageGroupId] += $item->getPageViews();
61
+ $totalPageViews +=$item->getPageViews();
62
+ if($item->getLoadTimeMax()>$maxLoad)
63
+ {
64
+ $maxLoad = $item->getLoadTimeMax();
65
+ $maxLoadTime = $item->getMeasureTime();
66
+ }
67
+ }
68
+ if(!$dataForCompare)
69
+ {
70
+ $this->getParentBlock()->setTotalPageViews($totalPageViews);
71
+ $this->getParentBlock()->setMaxLoad($maxLoad);
72
+ $this->getParentBlock()->setMaxLoadTime($maxLoadTime);
73
+ }
74
+
75
+ foreach($collection as $item)
76
+ {
77
+ $pageGroupId = $item->getData('page_group_id');
78
+ if(!isset($data[$pageGroupId]))
79
+ {
80
+ $data[$pageGroupId] = array('avg'=>0,'max'=>0);
81
+ }
82
+ if($item->getLoadTimeMax()>$data[$pageGroupId]['max'])
83
+ {
84
+ $data[$pageGroupId]['max'] = $item->getLoadTimeMax();
85
+ }
86
+ $data[$pageGroupId]['avg'] += $item->getLoadTimeAvg() * $item->getPageViews()/$totalPageViewsByGroup[$pageGroupId];
87
+
88
+ }
89
+ return $data;
90
+ }
91
+
92
+ /**
93
+ * Processes data and stores it as this object property
94
+ *
95
+ * @param array $data
96
+ * @param bool $dataCompare
97
+ */
98
+ private function _processDataForBar($data, $dataCompare)
99
+ {
100
+ $loadAvg = 'avg_load: [';
101
+ $loadMax = 'max_load: [';
102
+ $groups = Mage::helper('aitloadmon')->getGroupsArray();
103
+ $gcnt = 0;
104
+ foreach($groups as $pageGroupId)
105
+ {
106
+ if(!isset($data[$pageGroupId]))
107
+ {
108
+ $loadAvg .= '0';
109
+ $loadMax .= '0';
110
+ }
111
+ else
112
+ {
113
+ $loadAvg .= $data[$pageGroupId]['avg'];
114
+ $loadMax .= $data[$pageGroupId]['max'];
115
+ }
116
+
117
+ if($dataCompare)
118
+ {
119
+ if(!isset($dataCompare[$pageGroupId]))
120
+ {
121
+ $loadAvg .= ',0';
122
+ $loadMax .= ',0';
123
+ }
124
+ else
125
+ {
126
+ $loadAvg .= ','.$dataCompare[$pageGroupId]['avg'];
127
+ $loadMax .= ','.$dataCompare[$pageGroupId]['max'];
128
+ }
129
+ }
130
+
131
+ $gcnt++;
132
+ if($gcnt != count($groups))
133
+ {
134
+ $loadAvg .= ',';
135
+ $loadMax .= ',';
136
+ }
137
+ }
138
+ $loadAvg .= ']';
139
+ $loadMax .= ']';
140
+
141
+ $this->setBarGraphData($loadMax.','.$loadAvg);
142
+ }
143
+
144
+ /**
145
+ * Returns a JS array of labels
146
+ *
147
+ * @param bool $compareMode
148
+ * @return string
149
+ */
150
+ public function getBarLabels($compareMode)
151
+ {
152
+
153
+ $groups = array_keys(Mage::helper('aitloadmon')->getGroupsArray());
154
+ if($compareMode)
155
+ {
156
+ $glue = '\',\'\',\'';
157
+ }
158
+ else
159
+ {
160
+ $glue = '\',\'';
161
+ }
162
+
163
+
164
+ return '[\''.implode($glue, $groups).($compareMode?'\',\'':'').'\']';
165
+ }
166
+ }
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitloadmon/Filter.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_Aitloadmon_Filter extends Mage_Adminhtml_Block_Widget_Form
7
+ {
8
+ /**
9
+ * Preparing needed data for form
10
+ */
11
+ private function _prepareData()
12
+ {
13
+ $collection = $this->getParentBlock()->getCollection();
14
+
15
+ $totalPageViews = 0;
16
+ $maxLoad = 0;
17
+ $maxLoadTime = 0;
18
+
19
+ foreach($collection as $item)
20
+ {
21
+ $totalPageViews +=$item->getPageViews();
22
+ if($item->getLoadTimeMax()>$maxLoad)
23
+ {
24
+ $maxLoad = $item->getLoadTimeMax();
25
+ $maxLoadTime = $item->getMeasureTime();
26
+ }
27
+ }
28
+
29
+ $this->setTotalPageViews($totalPageViews);
30
+ $this->setMaxLoad($maxLoad);
31
+ $this->setMaxLoadTime($maxLoadTime);
32
+ }
33
+
34
+ /**
35
+ * Preparing form
36
+ *
37
+ * @return Mage_Adminhtml_Block_Widget_Form
38
+ */
39
+ protected function _prepareForm()
40
+ {
41
+ $this->_prepareData();
42
+
43
+ $form = new Varien_Data_Form(array(
44
+ 'id' => 'edit_form',
45
+ 'action' => $this->getUrl('*/*/index'),
46
+ 'method' => 'post',
47
+ )
48
+ );
49
+ $fieldset = $form->addFieldset('aitloadmon_form', array('legend'=>Mage::helper('aitloadmon')->__('Custom Filter Options')));
50
+
51
+ $fieldset->addField('starts', 'date', array(
52
+ 'name' => 'startDate',
53
+ 'title' => Mage::helper('aitloadmon')->__('Start Date'),
54
+ 'label' => Mage::helper('aitloadmon')->__('Start Date'),
55
+ 'image' => $this->getSkinUrl('images/grid-cal.gif'),
56
+ 'format' => 'yyyy-MM-dd HH:mm:ss',
57
+ 'value' => $this->getParentBlock()->getStartDate(),
58
+ 'time' => true,
59
+ 'required' => true,
60
+ ));
61
+
62
+ $fieldset->addField('ends', 'date', array(
63
+ 'name' => 'endDate',
64
+ 'title' => Mage::helper('aitloadmon')->__('End Date'),
65
+ 'label' => Mage::helper('aitloadmon')->__('End Date'),
66
+ 'image' => $this->getSkinUrl('images/grid-cal.gif'),
67
+ 'format' => 'yyyy-MM-dd HH:mm:ss',
68
+ 'value' => $this->getParentBlock()->getEndDate(),
69
+ 'time' => true,
70
+ 'required' => true,
71
+ ));
72
+
73
+
74
+ $fieldset->addField('type','select',array(
75
+ 'title' => Mage::helper('aitloadmon')->__('Graph displays:'),
76
+ 'label' => Mage::helper('aitloadmon')->__('Graph displays:'),
77
+ 'name' => 'type',
78
+ 'options' => array(
79
+ 'max' => Mage::helper('aitloadmon')->__('Maximum load'),
80
+ 'avg' => Mage::helper('aitloadmon')->__('Average load'),
81
+ ),
82
+ 'value' => $this->getParentBlock()->getType(),
83
+ ));
84
+
85
+
86
+ $compare = $fieldset->addField('compare','select',array(
87
+ 'title' => Mage::helper('aitloadmon')->__('Compare with the same period'),
88
+ 'label' => Mage::helper('aitloadmon')->__('Compare with the same period'),
89
+ 'name' => 'compare',
90
+ 'options' => array(
91
+ 0 => Mage::helper('aitloadmon')->__('No'),
92
+ 1 => Mage::helper('aitloadmon')->__('Yes'),
93
+ ),
94
+ 'value' => $this->getParentBlock()->getCompare(),
95
+ ));
96
+
97
+ $compareDate = $fieldset->addField('starts_compare', 'date', array(
98
+ 'name' => 'startDateCompare',
99
+ 'title' => Mage::helper('aitloadmon')->__('From Date'),
100
+ 'label' => Mage::helper('aitloadmon')->__('From Date'),
101
+ 'image' => $this->getSkinUrl('images/grid-cal.gif'),
102
+ 'format' => 'yyyy-MM-dd HH:mm:ss',
103
+ 'value' => $this->getParentBlock()->getStartDateCompare(),
104
+ 'time' => true,
105
+ ));
106
+
107
+
108
+ $fieldset->addField('submit1', 'submit', array(
109
+ 'name' => 'submit1',
110
+ 'title' => Mage::helper('aitloadmon')->__('Filter'),
111
+ 'value' => Mage::helper('aitloadmon')->__('Filter'),
112
+ 'class' => 'form-button',
113
+ ));
114
+
115
+ $fieldset = $form->addFieldset('aitloadmon_stat', array('legend'=>Mage::helper('aitloadmon')->__('Global stats for the selected period')));
116
+
117
+
118
+ $fieldset->addField('page_views', 'label', array(
119
+ 'label' => Mage::helper('aitloadmon')->__('Total page views'),
120
+ 'title' => Mage::helper('aitloadmon')->__('Total page views'),
121
+ 'value' => $this->getTotalPageViews(),
122
+ ));
123
+
124
+
125
+ $fieldset->addField('max_load', 'label', array(
126
+ 'label' => Mage::helper('aitloadmon')->__('Max load'),
127
+ 'title' => Mage::helper('aitloadmon')->__('Max load'),
128
+ 'value' => number_format($this->getMaxLoad(),2).' Sec',
129
+ ));
130
+
131
+ $fieldset->addField('max_load_time', 'label', array(
132
+ 'label' => Mage::helper('aitloadmon')->__('Max load time'),
133
+ 'title' => Mage::helper('aitloadmon')->__('Max load time'),
134
+ 'value' => $this->getMaxLoadTime(),
135
+ ));
136
+
137
+ $form->setUseContainer(true);
138
+ $this->setForm($form);
139
+ $this->setChild('form_after', $this->getLayout()->createBlock('adminhtml/widget_form_element_dependence')
140
+ ->addFieldMap($compare->getHtmlId(), $compare->getName())
141
+ ->addFieldMap($compareDate->getHtmlId(), $compareDate->getName())
142
+ ->addFieldDependence(
143
+ $compareDate->getName(),
144
+ $compare->getName(),
145
+ 1
146
+ )
147
+ );
148
+ return parent::_prepareForm();
149
+ }
150
+ }
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Aitloadmon/LineGraph.php ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_Aitloadmon_LineGraph extends Mage_Adminhtml_Block_Widget_Form
7
+ {
8
+
9
+ private $_maxLabelsInGraph = 20;
10
+
11
+ /**
12
+ * Gets collection(s) from parent block and processes data for javascript graphics
13
+ */
14
+ protected function _beforeToHtml()
15
+ {
16
+
17
+ $collection = $this->getParentBlock()->getCollection();
18
+ if($this->getCompare())
19
+ {
20
+ $collectionCompare = $this->getParentBlock()->getCompareCollection();
21
+ }
22
+
23
+ $type = $this->getType();
24
+ $lineData = $this->_preProcessDataForLine($collection);
25
+ $this->_processDataForLine($lineData, $type);
26
+
27
+ if($this->getCompare())
28
+ {
29
+ $lineDataCompare = $this->_preProcessDataForLine($collectionCompare);
30
+ $this->_processDataForLine($lineDataCompare, $type, true);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Getting the type of values we show in graph (average or maximum)
36
+ *
37
+ * @return mixed
38
+ */
39
+ public function getType()
40
+ {
41
+ return $this->getParentBlock()->getType();
42
+ }
43
+
44
+ /**
45
+ * Returns whether the compare mode is enabled
46
+ *
47
+ * @return bool
48
+ */
49
+ public function getCompare()
50
+ {
51
+ return $this->getParentBlock()->getCompare();
52
+ }
53
+
54
+ /**
55
+ * Returns html for graphics controls
56
+ *
57
+ * @return string
58
+ */
59
+ public function getCheckBoxes()
60
+ {
61
+ $groups = Mage::helper('aitloadmon')->getGroupsArray();
62
+ $html = '';
63
+ foreach($groups as $groupName => $groupValue)
64
+ {
65
+ $html .= '<label><input type="checkbox" checked="checked" rel="'.$groupValue.'" onclick="toggleLines(this);" class="linegraph_check"/>'.ucfirst($groupName).'</label>';
66
+ }
67
+ return $html;
68
+ }
69
+
70
+
71
+ /**
72
+ * Pre-processes data for further data processing
73
+ *
74
+ * @param Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon_Collection $collection
75
+ * @return array
76
+ */
77
+ private function _preProcessDataForLine($collection)
78
+ {
79
+ $data = array();
80
+ foreach($collection as $item)
81
+ {
82
+ $pageGroupId = $item->getData('page_group_id');
83
+ $time = $item->getData('measure_time');
84
+ if(!isset($data[$time]))
85
+ {
86
+ $data[$time] = array();
87
+ }
88
+ $data[$time][$pageGroupId]['avg'] = number_format($item->getLoadTimeAvg(),2);
89
+ $data[$time][$pageGroupId]['max'] = number_format($item->getLoadTimeMax(),2);
90
+ $data[$time][$pageGroupId]['pgv'] = $item->getPageViews();
91
+ }
92
+ return $data;
93
+ }
94
+
95
+ /**
96
+ * Processes data and stores it as this object property
97
+ * @param $data
98
+ * @param string $type
99
+ * @param bool $compare
100
+ */
101
+ private function _processDataForLine($data, $type = 'avg', $compare = false)
102
+ {
103
+ $groups = Mage::helper('aitloadmon')->getGroupsArray();
104
+ $dataString = '';
105
+ $dataSizeString = '';
106
+ $gcnt = 0;
107
+ foreach($groups as $pageGroupCode => $pageGroupId)
108
+ {
109
+ $dataString .= $pageGroupCode.': [';
110
+ $dataSizeString .= $pageGroupId.': [';
111
+ $dcnt = 0;
112
+ foreach($data as $timeData)
113
+ {
114
+ if(!isset($timeData[$pageGroupId][$type]))
115
+ {
116
+ $dataString .= '0';
117
+ $dataSizeString .= '0';
118
+ }
119
+ else
120
+ {
121
+ $dataString .= $timeData[$pageGroupId][$type];
122
+ $dataSizeString .= $timeData[$pageGroupId]['pgv'];
123
+ }
124
+ $dcnt++;
125
+ if($dcnt != count($data))
126
+ {
127
+ $dataString .= ',';
128
+ $dataSizeString .= ',';
129
+ }
130
+ }
131
+ $dataString .= ']';
132
+ $dataSizeString .= ']';
133
+ $gcnt++;
134
+ if($gcnt != count($groups))
135
+ {
136
+ $dataString .= ',';
137
+ $dataSizeString .= ',';
138
+ }
139
+ }
140
+
141
+ $method = 'setLineGraphData';
142
+ if($compare)
143
+ {
144
+ $method .= 'Compared';
145
+ }
146
+
147
+ $times = $this->_prepareHorizontalLabels(array_keys($data));
148
+ $this->$method(array('data'=>$dataString,'labels'=>'[\''.implode('\',\'',$times).'\']','dot_labels'=>'[\''.implode('\',\'',array_keys($data)).'\']','dataSize'=>$dataSizeString));
149
+ }
150
+
151
+ /**
152
+ * Preparing horizontal labels for graph
153
+ *
154
+ * @param array $labelArray
155
+ * @return array
156
+ */
157
+ private function _prepareHorizontalLabels($labelArray = array())
158
+ {
159
+ $labelArrayCount = count($labelArray);
160
+ if($labelArrayCount>$this->_maxLabelsInGraph)
161
+ {
162
+ $each = 2;
163
+ while(($labelArrayCount/$each) > $this->_maxLabelsInGraph)
164
+ {
165
+ $each++;
166
+ }
167
+ $cnt = 0;
168
+ foreach($labelArray as $key=>$value)
169
+ {
170
+ $cnt++;
171
+ if($cnt==$each)
172
+ {
173
+ $cnt=0;
174
+ }
175
+ else
176
+ {
177
+ $labelArray[$key]='';
178
+ }
179
+ }
180
+ }
181
+
182
+ $currentDate = '';
183
+ foreach($labelArray as $key=>$value)
184
+ {
185
+ if($value=='')
186
+ {
187
+ continue;
188
+ }
189
+
190
+ list($date,$time) = explode(' ',$value);
191
+ if($currentDate != $date)
192
+ {
193
+ $currentDate = $date;
194
+ $labelArray[$key]=$time.'\n\r'.$date;
195
+ }
196
+ else
197
+ {
198
+ $labelArray[$key]=$time;
199
+ }
200
+ }
201
+
202
+ return $labelArray;
203
+ }
204
+ }
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Calculator.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_Calculator extends Mage_Adminhtml_Block_Widget_Form
7
+ {
8
+ /**
9
+ * Preparing form for the block
10
+ *
11
+ * @return Mage_Adminhtml_Block_Widget_Form|void
12
+ */
13
+ protected function _prepareForm()
14
+ {
15
+ $form = new Varien_Data_Form(array(
16
+ 'id' => 'edit_form',
17
+ 'action' => $this->getUrl('*/calculator/submit'),
18
+ 'method' => 'post',
19
+ 'enctype' => 'multipart/form-data',
20
+ )
21
+ );
22
+ $fieldset = $form->addFieldset('aitloadmon_form', array('legend'=>Mage::helper('aitloadmon')->__('Source selection')));
23
+ $source = $fieldset->addField('source','select',array(
24
+ 'title' => Mage::helper('aitloadmon')->__('Please select source for exemplary server load'),
25
+ 'label' => Mage::helper('aitloadmon')->__('Please select source for exemplary server load'),
26
+ 'name' => 'source',
27
+ 'options' => array(
28
+ 'monitor' => Mage::helper('aitloadmon')->__('Server Load Monitor'),
29
+ 'log' => Mage::helper('aitloadmon')->__('Magento Logs'),
30
+ 'analytics' => Mage::helper('aitloadmon')->__('Google Analytics File'),
31
+ ),
32
+ ));
33
+ $file = $fieldset->addField('csv_file', 'file', array(
34
+ 'label' => Mage::helper('aitloadmon')->__('Google Analytics File'),
35
+ 'required' => true,
36
+ 'name' => 'csv_file',
37
+ ));
38
+ $fieldset->addField('submit', 'submit', array(
39
+ 'name' => 'submit',
40
+ 'title' => Mage::helper('aitloadmon')->__('Submit'),
41
+ 'value' => Mage::helper('aitloadmon')->__('Submit'),
42
+ 'class' => 'form-button',
43
+ 'onclick' => 'var validator = new Validation(\'edit_form\');if (!validator.validate()) {return false;}',
44
+ ));
45
+
46
+ $form->setUseContainer(true);
47
+ $this->setForm($form);
48
+
49
+ $this->setChild('form_after', $this->getLayout()->createBlock('adminhtml/widget_form_element_dependence')
50
+ ->addFieldMap($source->getHtmlId(), $source->getName())
51
+ ->addFieldMap($file->getHtmlId(), $file->getName())
52
+ ->addFieldDependence(
53
+ $file->getName(),
54
+ $source->getName(),
55
+ 'analytics'
56
+ )
57
+ );
58
+ }
59
+ }
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/CalculatorResult.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_CalculatorResult extends Mage_Adminhtml_Block_Widget_Form
7
+ {
8
+ /**
9
+ * Preparing form for the block
10
+ *
11
+ * @return Mage_Adminhtml_Block_Widget_Form|void
12
+ */
13
+ protected function _prepareForm()
14
+ {
15
+ $form = new Varien_Data_Form(array(
16
+ 'id' => 'edit_form',
17
+ 'action' => $this->getUrl('*/calculator/submit'),
18
+ 'method' => 'post',
19
+ 'enctype' => 'multipart/form-data',
20
+ )
21
+ );
22
+ $fieldset = $form->addFieldset('aitloadmon_form', array('legend'=>Mage::helper('aitloadmon')->__('Data to enter in server calculator')));
23
+
24
+ $fieldset->addField('products', 'label', array(
25
+ 'label' => Mage::helper('aitloadmon')->__('Product number'),
26
+ 'title' => Mage::helper('aitloadmon')->__('Product number'),
27
+ 'value' => $this->getProducts(),
28
+ ));
29
+
30
+ $fieldset->addField('max_load_time', 'label', array(
31
+ 'label' => Mage::helper('aitloadmon')->__('Exemplary server load'),
32
+ 'title' => Mage::helper('aitloadmon')->__('Exemplary server load'),
33
+ 'value' => $this->getLoad(),
34
+ ));
35
+
36
+ $fieldset->addField('back', 'button', array(
37
+ 'value' => Mage::helper('aitloadmon')->__('Go Back'),
38
+ 'onclick' => 'document.location.href=\''.$this->getUrl('*/calculator/index').'\'',
39
+ 'class' => 'form-button submit',
40
+ ));
41
+
42
+ $this->setForm($form);
43
+ }
44
+ }
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Indicator.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_Indicator extends Mage_Adminhtml_Block_Template
7
+ {
8
+ /**
9
+ * Returns an array of colors to use for different load levels in indicator
10
+ *
11
+ * @return string
12
+ */
13
+ private function _getColors()
14
+ {
15
+ return array(
16
+ Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_GREEN => '#1B7B00',
17
+ Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_YELLOW => '#f5861a',
18
+ Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_RED => '#a0151e',
19
+ Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_BLACK => '#000000',
20
+ );
21
+ }
22
+
23
+ /**
24
+ * Returns the color of the current load level or a green color whe the load level is unavailable for some reason
25
+ *
26
+ * @return string
27
+ */
28
+ public function getColor()
29
+ {
30
+ $colors = $this->_getColors();
31
+ $loadLevel = Mage::helper('aitloadmon')->getLoadLevel();
32
+ if($loadLevel && isset($colors[$loadLevel]))
33
+ {
34
+ return $colors[$loadLevel];
35
+ }
36
+ return $colors[Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_GREEN];
37
+ }
38
+ }
app/code/local/Aitoc/Aitloadmon/Block/Adminhtml/Manage.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Block_Adminhtml_Manage extends Mage_Adminhtml_Block_Widget_Form
7
+ {
8
+ /**
9
+ * Returns the compress date types
10
+ *
11
+ * @return array
12
+ */
13
+ private function _getCompressTypes()
14
+ {
15
+ return Mage::getModel('aitloadmon/compressSource')->toArray();
16
+ }
17
+
18
+ /**
19
+ * Preparing form for the block
20
+ *
21
+ * @return Mage_Adminhtml_Block_Widget_Form|void
22
+ */
23
+ protected function _prepareForm()
24
+ {
25
+ $settings = Mage::getModel('aitloadmon/manage')->getSettings();
26
+
27
+ $form = new Varien_Data_Form(array(
28
+ 'id' => 'manageData_form',
29
+ 'action' => $this->getUrl('*/*/manage'),
30
+ 'method' => 'post',
31
+ )
32
+ );
33
+
34
+ $fieldset = $form->addFieldset('manage_stat', array('legend'=>Mage::helper('aitloadmon')->__('Data stats').'<span style="font-size:10px;margin-left:20px;">('.Mage::helper('aitloadmon')->__('Server Load Monitor collects data from CMS, Checkout, Catalog, Catalogsearch and Other groups of pages every single minute by cron. It stores 60*5 values per hour, 24*60*5 per 24 hours.').')</span>'));
35
+
36
+ $fieldset->addField('total_records', 'label', array(
37
+ 'label' => Mage::helper('aitloadmon')->__('Total records'),
38
+ 'title' => Mage::helper('aitloadmon')->__('Total records'),
39
+ 'value' => Mage::getModel('aitloadmon/aitloadmon')->getResource()->getRows(),
40
+ ));
41
+
42
+ $fieldset->addField('total_size', 'label', array(
43
+ 'label' => Mage::helper('aitloadmon')->__('Total size in bytes'),
44
+ 'title' => Mage::helper('aitloadmon')->__('Total size in bytes'),
45
+ 'value' => Mage::getModel('aitloadmon/aitloadmon')->getResource()->getWeight(),
46
+ ));
47
+
48
+ $fieldset = $form->addFieldset('manage_auto', array('legend'=>Mage::helper('aitloadmon')->__('Automatic data compression').'<span style="font-size:10px;margin-left:20px;">('.Mage::helper('aitloadmon')->__('In order not to overload database Server Load Monitor compresses data automatically.').')</span>'));
49
+
50
+ $fieldset->addField('auto_enable','select', array(
51
+ 'label' => Mage::helper('aitloadmon')->__('Enable automatic data compression'),
52
+ 'title' => Mage::helper('aitloadmon')->__('Enable automatic data compression'),
53
+ 'name' => 'auto_enable',
54
+ 'options' => array(
55
+ 0 => Mage::helper('aitloadmon')->__('No'),
56
+ 1 => Mage::helper('aitloadmon')->__('Yes'),
57
+ ),
58
+ 'value' => isset($settings['enabled'])?$settings['enabled']:1,
59
+ ));
60
+
61
+ $fieldset->addField('auto_day','select', array(
62
+ 'label' => Mage::helper('aitloadmon')->__('Compress data of last day to'),
63
+ 'title' => Mage::helper('aitloadmon')->__('Compress data of last day to'),
64
+ 'name' => 'auto_day',
65
+ 'options' => $this->_getCompressTypes(),
66
+ 'value' => isset($settings['day'])?$settings['day']:1,
67
+ ));
68
+
69
+ $fieldset->addField('auto_month','select', array(
70
+ 'label' => Mage::helper('aitloadmon')->__('Compress data of last month to'),
71
+ 'title' => Mage::helper('aitloadmon')->__('Compress data of last month to'),
72
+ 'name' => 'auto_month',
73
+ 'options' => $this->_getCompressTypes(),
74
+ 'value' => isset($settings['month'])?$settings['month']:2,
75
+ ));
76
+
77
+ $fieldset->addField('auto_year','select', array(
78
+ 'label' => Mage::helper('aitloadmon')->__('Compress data of last year to'),
79
+ 'title' => Mage::helper('aitloadmon')->__('Compress data of last year to'),
80
+ 'name' => 'auto_year',
81
+ 'options' => $this->_getCompressTypes(),
82
+ 'value' => isset($settings['year'])?$settings['year']:3,
83
+ ));
84
+
85
+ $fieldset->addField('submit_auto', 'submit', array(
86
+ 'name' => 'submit_auto',
87
+ 'title' => Mage::helper('aitloadmon')->__('Save settings'),
88
+ 'value' => Mage::helper('aitloadmon')->__('Save settings'),
89
+ 'class' => 'form-button',
90
+ ));
91
+
92
+ $fieldset = $form->addFieldset('manage_manual', array('legend'=>Mage::helper('aitloadmon')->__('Manual data compression').'<span style="font-size:10px;margin-left:20px;">('.Mage::helper('aitloadmon')->__('Default compression settings are optimized and recommended but Admin is enabled to choose preferred compression settings as well as compress the data manually.').')</span>'));
93
+
94
+ $fieldset->addField('manual_from', 'date', array(
95
+ 'name' => 'manual_from',
96
+ 'title' => Mage::helper('aitloadmon')->__('From date'),
97
+ 'label' => Mage::helper('aitloadmon')->__('From date'),
98
+ 'image' => $this->getSkinUrl('images/grid-cal.gif'),
99
+ 'format' => 'yyyy-MM-dd HH:mm:ss',
100
+ 'value' => date('Y-m-d H:i:s', time()-2*24*3600),
101
+ 'time' => true,
102
+ ));
103
+
104
+ $fieldset->addField('manual_to', 'date', array(
105
+ 'name' => 'manual_to',
106
+ 'title' => Mage::helper('aitloadmon')->__('To date'),
107
+ 'label' => Mage::helper('aitloadmon')->__('To date'),
108
+ 'image' => $this->getSkinUrl('images/grid-cal.gif'),
109
+ 'format' => 'yyyy-MM-dd HH:mm:ss',
110
+ 'value' => date('Y-m-d H:i:s', time()-24*3600),
111
+ 'time' => true,
112
+ ));
113
+
114
+ $fieldset->addField('manual_compress','select', array(
115
+ 'label' => Mage::helper('aitloadmon')->__('Compress data to'),
116
+ 'title' => Mage::helper('aitloadmon')->__('Compress data to'),
117
+ 'name' => 'manual_compress',
118
+ 'options' => $this->_getCompressTypes(),
119
+ 'value' => 1,
120
+ ));
121
+
122
+ $fieldset->addField('submit_manual', 'submit', array(
123
+ 'name' => 'submit_manual',
124
+ 'title' => Mage::helper('aitloadmon')->__('Compress data'),
125
+ 'value' => Mage::helper('aitloadmon')->__('Compress data'),
126
+ 'class' => 'form-button',
127
+ ));
128
+
129
+ $form->setUseContainer(true);
130
+ $this->setForm($form);
131
+ }
132
+
133
+ }
app/code/local/Aitoc/Aitloadmon/Helper/Data.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Helper_Data extends Mage_Core_Helper_Abstract
7
+ {
8
+ /**
9
+ * Returns an array of page groups we are dividing the page views
10
+ *
11
+ * @return array
12
+ */
13
+ public function getGroupsArray()
14
+ {
15
+ return array(
16
+ 'other' => 1,
17
+ 'cms' => 2,
18
+ 'checkout' => 3,
19
+ 'catalogsearch' => 4,
20
+ 'catalog' => 5,
21
+ );
22
+ }
23
+
24
+ /**
25
+ * Gets current load level
26
+ *
27
+ * @return int
28
+ */
29
+ public function getLoadLevel()
30
+ {
31
+ return Aitoc_Aitloadmon_Collect::getLoadLevel();
32
+ }
33
+
34
+
35
+ /**
36
+ * Gets current page group
37
+ *
38
+ * @return int|bool
39
+ */
40
+ public function getGroupIdOfCurrentPage()
41
+ {
42
+ $module = Mage::app()->getRequest()->getModuleName();
43
+ $groupIds = Mage::helper('aitloadmon')->getGroupsArray();
44
+ if($module == 'admin')
45
+ {
46
+ return false;
47
+ }
48
+ elseif(isset($groupIds[$module]))
49
+ {
50
+ return $groupIds[$module];
51
+ }
52
+ else
53
+ {
54
+ return 1;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Sets current load level
60
+ * @param int $loadLevel
61
+ */
62
+ public function setLoadLevel($loadLevel)
63
+ {
64
+ Aitoc_Aitloadmon_Collect::setLoadLevel($loadLevel);
65
+ }
66
+
67
+ }
app/code/local/Aitoc/Aitloadmon/Model/Aitloadmon.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Aitloadmon extends Mage_Core_Model_Abstract
7
+ {
8
+ const LOAD_LEVEL_GREEN = 1;
9
+ const LOAD_LEVEL_YELLOW = 2;
10
+ const LOAD_LEVEL_RED = 3;
11
+ const LOAD_LEVEL_BLACK = 4;
12
+
13
+ /**
14
+ * Standard model constructor
15
+ */
16
+ public function _construct()
17
+ {
18
+ parent::_construct();
19
+ $this->_init('aitloadmon/aitloadmon');
20
+ }
21
+
22
+ /**
23
+ * Gets a data collection bounded by 2 dates
24
+ *
25
+ * @param $startDate
26
+ * @param $endDate
27
+ * @return Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon_Collection
28
+ */
29
+ public function getCollectionByDates($startDate, $endDate)
30
+ {
31
+ return $this->getCollection()->filterByDates($startDate, $endDate);
32
+ }
33
+
34
+ }
app/code/local/Aitoc/Aitloadmon/Model/Analytics.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Analytics
7
+ {
8
+ /**
9
+ * Gets the max number of visitors per minute parsing google analytics file
10
+ *
11
+ * @param string $filename
12
+ * @return int
13
+ */
14
+ public function getVisitors($filename)
15
+ {
16
+ $f = fopen($filename,"r");
17
+ $max = 0;
18
+ while($line = fgetcsv($f,0,','))
19
+ {
20
+ if(count($line)==2 && $line[0]!='' && isset($line[1]))
21
+ {
22
+ if($line[1]>$max)
23
+ {
24
+ $max = $line[1];
25
+ }
26
+ }
27
+ }
28
+ fclose($f);
29
+
30
+ return ceil($max/60);
31
+ }
32
+ }
app/code/local/Aitoc/Aitloadmon/Model/Calculator.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Calculator extends Mage_Core_Model_Abstract
7
+ {
8
+
9
+ private $_apiUrl = 'http://www.aitoc.com/api/xmlrpc/statistic/';
10
+
11
+ private $_infoClasses = array('magento','hardware','software');
12
+ private $_period = '1 WEEK';
13
+
14
+ /**
15
+ * Standard model constructor
16
+ */
17
+ public function _construct()
18
+ {
19
+ parent::_construct();
20
+ $this->_init('aitloadmon/calculator');
21
+ }
22
+
23
+ /**
24
+ * Getter for use in calculator results
25
+ *
26
+ * @return int
27
+ */
28
+ public function getProductInfo()
29
+ {
30
+ return Mage::getModel('catalog/product')->getCollection()->getSize();
31
+ }
32
+
33
+ /**
34
+ * Collects the info that will be sent to Server Calculator
35
+ *
36
+ * @return array
37
+ */
38
+ private function _collectSendingInfo()
39
+ {
40
+ $data = array();
41
+ foreach($this->_infoClasses as $class)
42
+ {
43
+ $model = Mage::getModel('aitloadmon/calculator_'.$class);
44
+ if($model instanceof Aitoc_Aitloadmon_Model_Calculator_Abstract)
45
+ {
46
+ $data += $model->toArray();
47
+ }
48
+ }
49
+ return $data;
50
+ }
51
+
52
+ /**
53
+ * Sends the info required for server calculator and clears the load info table
54
+ */
55
+ public function sendCalcInfo()
56
+ {
57
+ $client = new Zend_XmlRpc_Client($this->_apiUrl);
58
+ try{
59
+ $client->call('call', array('1234567890', 'aitcalc.addStatistic', array($this->_collectSendingInfo())));
60
+ }catch(Exception $e){
61
+ Mage::log('Data sending error in '.__METHOD__.': '.$e->getMessage());
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Gets the maximum number of page views per minute based on Magento logs
67
+ *
68
+ * @return int
69
+ */
70
+ public function getLogVisitors()
71
+ {
72
+ $collection = Mage::getModel('aitloadmon/dblog')->getCollection();
73
+ $collection->getSelect()
74
+ ->reset(Zend_Db_Select::COLUMNS)
75
+ ->columns('COUNT(*) AS visitors')
76
+ ->where('visit_time > DATE_SUB(NOW(), INTERVAL '.$this->_period.')')
77
+ ->group('DATE_FORMAT(visit_time,"%y-%m-%d-%H-%i")');
78
+ $max = 0;
79
+ foreach($collection as $item)
80
+ {
81
+ if($item->getVisitors()>$max)
82
+ {
83
+ $max = $item->getVisitors();
84
+ }
85
+ }
86
+ return $max;
87
+ }
88
+
89
+ /**
90
+ * Gets the maximum number of page views per minute based on monitor
91
+ *
92
+ * @return int
93
+ */
94
+ public function getMonitorVisitors()
95
+ {
96
+ $collection = Mage::getModel('aitloadmon/aitloadmon')->getCollection();
97
+ $collection->getSelect()
98
+ ->reset(Zend_Db_Select::COLUMNS)
99
+ ->columns('SUM(max_page_views_per_minute) AS visitors')
100
+ ->where('measure_time > DATE_SUB(NOW(), INTERVAL '.$this->_period.')')
101
+ ->group('DATE_FORMAT(measure_time,"%y-%m-%d-%H-%i")');
102
+
103
+ $max = 0;
104
+ foreach($collection as $item)
105
+ {
106
+ if($item->getVisitors()>$max)
107
+ {
108
+ $max = $item->getVisitors();
109
+ }
110
+ }
111
+ return $max;
112
+ }
113
+
114
+
115
+ /**
116
+ * Gets the maximum number of page views per minute based on Google Analytics file
117
+ *
118
+ * @return int|null
119
+ */
120
+ public function getAnalyticsVisitors()
121
+ {
122
+ if(file_exists($this->getCsvFile()))
123
+ {
124
+ return Mage::getModel('aitloadmon/analytics')->getVisitors($this->getCsvFile());
125
+ }
126
+
127
+ return null;
128
+ }
129
+ }
app/code/local/Aitoc/Aitloadmon/Model/Calculator/Abstract.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ abstract class Aitoc_Aitloadmon_Model_Calculator_Abstract
7
+ {
8
+ protected $_infoMethodPattern = '/_get\w+Info/';
9
+
10
+ /**
11
+ * Executes a system command and returns its output
12
+ *
13
+ * @param $command
14
+ * @return string
15
+ */
16
+ protected function _exec($command)
17
+ {
18
+ $data = array();
19
+ @exec($command, $data);
20
+ if(count($data))
21
+ {
22
+ return implode("\r\n",$data);
23
+ }
24
+ else
25
+ {
26
+ return '';
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Safely gets the contents of a file
32
+ *
33
+ * @param string $file
34
+ * @return null|string
35
+ */
36
+ protected function _getFileSafe($file)
37
+ {
38
+ try{
39
+ if($data = @file_get_contents($file))
40
+ {
41
+ return $data;
42
+ }
43
+ if($data = $this->_exec('cat '.$file))
44
+ {
45
+ return $data;
46
+ }
47
+ }
48
+ catch(Exception $e)
49
+ {
50
+ return null;
51
+ }
52
+ return null;
53
+ }
54
+
55
+ /**
56
+ * Finds the methods that return required and returns them
57
+ *
58
+ * @return array
59
+ */
60
+ protected function _getInfoMethods()
61
+ {
62
+ $methods = get_class_methods(get_class($this));
63
+ $infoMethods = array();
64
+ foreach($methods as $method)
65
+ {
66
+ if(preg_match($this->_infoMethodPattern,$method))
67
+ {
68
+ $infoMethods[] = $method;
69
+ }
70
+ }
71
+
72
+ return $infoMethods;
73
+ }
74
+
75
+ /**
76
+ * Returns the required info as a joined array
77
+ *
78
+ * @return array
79
+ */
80
+ public function toArray()
81
+ {
82
+ $returnArray = array();
83
+ foreach($this->_getInfoMethods() as $method)
84
+ {
85
+ $returnArray += $this->$method();
86
+ }
87
+
88
+ return $returnArray;
89
+ }
90
+ }
app/code/local/Aitoc/Aitloadmon/Model/Calculator/Hardware.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Calculator_Hardware extends Aitoc_Aitloadmon_Model_Calculator_Abstract
7
+ {
8
+ private $_linuxCpuFile = '/proc/cpuinfo';
9
+ private $_linuxMemFile = '/proc/meminfo';
10
+
11
+ /**
12
+ * Gets the CPU info of a server
13
+ *
14
+ * @return array
15
+ */
16
+ protected function _getCpuInfo()
17
+ {
18
+ $info = $this->_getFileSafe($this->_linuxCpuFile);
19
+ if($info)
20
+ {//^(processor\s+:\s+\d+)\s*$.*?
21
+ $freqs = array();
22
+ $bogomipss = array();
23
+ $modelNames = array();
24
+ if(preg_match_all('/^cpu\sMHz\s+:\s+([\d\.]+)\s*$/ms',$info,$matches))
25
+ {
26
+ $freqs = $matches[1];
27
+ }
28
+ if(preg_match_all('/^bogomips\s+:\s+([\d\.]+)\s*$/ms',$info,$matches))
29
+ {
30
+ $bogomipss = $matches[1];
31
+ }
32
+ if(preg_match_all('/^model\sname\s+:\s+([\w\d\s\(\)]+)\s*$/ms',$info,$matches))
33
+ {
34
+ $modelNames = $matches[1];
35
+ }
36
+ return array('cpu'=>array('cores'=>count($freqs),'freq'=>max($freqs),'bogomips'=>max($bogomipss),'model name'=>isset($modelNames[0])?$modelNames[0]:''));
37
+ }
38
+
39
+ return null;
40
+ }
41
+
42
+ /**
43
+ * Gets the memory info of a server
44
+ *
45
+ * @return array|null
46
+ */
47
+ protected function _getMemInfo()
48
+ {
49
+ $info = $this->_getFileSafe($this->_linuxMemFile);
50
+ if($info)
51
+ {
52
+ if(preg_match('/MemTotal:(.*?B)/si',$info,$matches))
53
+ {
54
+ return array('mem'=>trim($matches[1]));
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ /**
61
+ * Gets if the server is virual
62
+ *
63
+ * @return array
64
+ */
65
+ protected function _getVirtualInfo()
66
+ {//todo think of the additional ways
67
+ return array('virtual'=>file_exists('/proc/vz/veinfo')?'true':'false');
68
+ }
69
+ }
app/code/local/Aitoc/Aitloadmon/Model/Calculator/Magento.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Calculator_Magento extends Aitoc_Aitloadmon_Model_Calculator_Abstract
7
+ {
8
+ private $_interval = '5';
9
+
10
+ /**
11
+ * Gets the system cache types
12
+ *
13
+ * @return array
14
+ */
15
+ protected function _getSystemcacheInfo()
16
+ {
17
+ return array('system_cache' => array('backend' => (string)Mage::app()->getConfig()->getNode('global/cache/backend'), 'slow_backend' => (string)Mage::app()->getConfig()->getNode('global/cache/slow_backend')));
18
+ }
19
+
20
+ /**
21
+ * Gets the statuses of caches
22
+ *
23
+ * @return array
24
+ */
25
+ protected function _getCacheInfo()
26
+ {
27
+ $caches = array();
28
+ foreach (Mage::app()->getCacheInstance()->getTypes() as $type) {
29
+ $caches[$type->getId()] = $type->getStatus();
30
+ }
31
+ return array('caches' => $caches);
32
+ }
33
+
34
+
35
+ /**
36
+ * Gets the compiler status
37
+ *
38
+ * @return array
39
+ */
40
+ protected function _getCompilerInfo()
41
+ {
42
+ return array('compiler' => defined('COMPILER_INCLUDE_PATH'));
43
+ }
44
+
45
+ /**
46
+ * Gets the number of customers
47
+ *
48
+ * @return array
49
+ */
50
+ protected function _getCustomerInfo()
51
+ {
52
+ return array('customers'=>Mage::getModel('customer/customer')->getCollection()->getSize());
53
+ }
54
+
55
+ /**
56
+ * Gets the number of products
57
+ *
58
+ * @return array
59
+ */
60
+ protected function _getProductInfo()
61
+ {
62
+ return array('products'=>Mage::getModel('catalog/product')->getCollection()->getSize());
63
+ }
64
+
65
+ /**
66
+ * Gets the number of quotes
67
+ *
68
+ * @return array
69
+ */
70
+ protected function _getQuoteInfo()
71
+ {
72
+ return array('quotes'=>Mage::getModel('sales/quote')->getCollection()->getSize());
73
+ }
74
+
75
+ /**
76
+ * Gets the number of orders
77
+ *
78
+ * @return array
79
+ */
80
+ protected function _getOrderInfo()
81
+ {
82
+ return array('orders'=>Mage::getModel('sales/order')->getCollection()->getSize());
83
+ }
84
+
85
+ /**
86
+ * Gets Magento version
87
+ *
88
+ * @return array
89
+ */
90
+ protected function _getVersionInfo()
91
+ {
92
+ return array('version'=>Mage::getVersion());
93
+ }
94
+
95
+ /**
96
+ * Gets the info about server load
97
+ *
98
+ * @return array
99
+ */
100
+ protected function _getLoadInfo()
101
+ {
102
+ $collection = Mage::getModel('aitloadmon/calculator')->getCollection();
103
+ $colString = '';
104
+ for($i=1; $i<6; $i++)
105
+ {
106
+ $colString .= 'MAX(pg'.$i.'_avg_load) AS pg'.$i.'_avg_load, pg'.$i.'_views, ';
107
+ }
108
+ $collection->getSelect()
109
+ ->reset(Zend_Db_Select::COLUMNS)
110
+ ->columns($colString.'CEIL(max_page_views_per_minute/'.$this->_interval.')*'.$this->_interval.' AS views, MAX(load_time_avg) AS load')
111
+ ->group('views');
112
+ $data = array();
113
+ foreach($collection as $item)
114
+ {
115
+ $data[$item->getViews()] = array('avg_load'=>$item->getLoad());
116
+ for($i=1; $i<6; $i++)
117
+ {
118
+ $key = 'pg'.$i;
119
+ $data[$item->getViews()][$key.'_avg_load'] = $item->getData($key.'_avg_load');
120
+ $data[$item->getViews()][$key.'_views'] = $item->getData($key.'_views');
121
+ }
122
+ }
123
+ Mage::getModel('aitloadmon/calculator')->getResource()->deleteAll();
124
+ return array('load'=>$data);
125
+ }
126
+
127
+ /**
128
+ * Gets the info whether the booster is installed
129
+ *
130
+ * @return array
131
+ */
132
+ protected function _getBoosterInfo()
133
+ {
134
+ $enabled = 'false';
135
+ $config = @simplexml_load_file(Mage::getBaseDir().DS.'app'.DS.'etc'.DS.'modules'.DS.'Aitoc_Aitpagecache.xml');
136
+ if ($config){
137
+ $enabled = (string)$config->modules->Aitoc_Aitpagecache->active;
138
+ }
139
+
140
+ return array('booster'=>$enabled);
141
+ }
142
+ }
app/code/local/Aitoc/Aitloadmon/Model/Calculator/Software.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Calculator_Software extends Aitoc_Aitloadmon_Model_Calculator_Abstract
7
+ {
8
+ protected $_acceleratorInis = array('eAccelerator'=>'eaccelerator.enable','APC'=>'apc.enabled','xCache'=>'xcache.cacher','Zend Optimizer+'=>'zend_optimizer.optimization_level','WinCache'=>'wincache.ocenabled');
9
+ protected $_osInfoFiles = array('/etc/SUSE-release','/etc/redhat-release','/etc/redhat_version','/etc/fedora-release','/etc/slackware-release','/etc/slackware-version',
10
+ '/etc/debian_release','/etc/debian_version','/etc/mandrake-release','/etc/yellowdog-release','/etc/sun-release','/etc/release','/etc/gentoo-release','/etc/UnitedLinux-release',
11
+ '/etc/lsb-release'
12
+ );
13
+
14
+ /**
15
+ * Gets OS info from system files
16
+ *
17
+ * @return null|string
18
+ */
19
+ protected function _readOsInfoFromFiles()
20
+ {
21
+ foreach($this->_osInfoFiles as $file)
22
+ {
23
+ if($data = $this->_getFileSafe($file))
24
+ {
25
+ if(strlen($data)>256)
26
+ {
27
+ $data = substr($data,0,256);
28
+ }
29
+ break;
30
+ }
31
+ }
32
+ return $data;
33
+ }
34
+
35
+ /**
36
+ * Gets the OS info
37
+ *
38
+ * @return array
39
+ */
40
+ protected function _getOsInfo()
41
+ {
42
+ if(!($os = $this->_exec('lsb_release -a')) && !($os = $this->_readOsInfoFromFiles()))
43
+ {
44
+ $os = php_uname();
45
+ }
46
+ return array('os'=>$os);
47
+ }
48
+
49
+
50
+ /**
51
+ * Get webserver type
52
+ *
53
+ * @return array
54
+ */
55
+ protected function _getWebserverInfo()
56
+ {
57
+ return array('webserver' => isset($_SERVER["SERVER_SOFTWARE"])?$_SERVER["SERVER_SOFTWARE"]:'');
58
+ }
59
+
60
+ /**
61
+ * Get MySQL version
62
+ *
63
+ * @return array
64
+ */
65
+ protected function _getMysqlInfo()
66
+ {
67
+ $resource = Mage::getSingleton('core/resource');
68
+ $readConnection = $resource->getConnection('core_read');
69
+ $query = 'SELECT VERSION()';
70
+ return array('mysql' => $readConnection->fetchOne($query));
71
+ }
72
+
73
+ /**
74
+ * Get PHP information
75
+ *
76
+ * @return array
77
+ */
78
+ protected function _getPhpInfo()
79
+ {
80
+
81
+ return array('php' => array('version' => phpversion(),'memory_limit' => ini_get('memory_limit'), 'server_api' => php_sapi_name()));
82
+ }
83
+
84
+ /**
85
+ * Get PHP Accelerator information
86
+ *
87
+ * @return array
88
+ */
89
+ protected function _getPhpAccelInfo()
90
+ {
91
+ $accelerator = '';
92
+ foreach($this->_acceleratorInis as $name => $setting)
93
+ {
94
+ if(ini_get($setting))
95
+ {
96
+ $accelerator = $name;
97
+ break;
98
+ }
99
+ }
100
+
101
+ return array('php_accel' => $accelerator);
102
+ }
103
+
104
+ }
app/code/local/Aitoc/Aitloadmon/Model/CompressSource.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_CompressSource
7
+ {
8
+
9
+ /**
10
+ * Returns compress types as a simple array
11
+ *
12
+ * @return array
13
+ */
14
+ public function getCompressTypesArray()
15
+ {
16
+ return array(
17
+ 1 => Mage::helper('aitloadmon')->__('hour'),
18
+ 2 => Mage::helper('aitloadmon')->__('day'),
19
+ 3 => Mage::helper('aitloadmon')->__('month'),
20
+ );
21
+ }
22
+
23
+
24
+ /**
25
+ * Returns compress types as an array for settings
26
+ *
27
+ * @return array
28
+ */
29
+ public function toOptionArray()
30
+ {
31
+ $levels = $this->getCompressTypesArray();
32
+
33
+ foreach($levels as $key=>$value)
34
+ {
35
+ $array[] = array('value' => $key, 'label'=>Mage::helper('aitloadmon')->__(ucfirst($value)));
36
+ }
37
+
38
+ return $array;
39
+ }
40
+
41
+ /**
42
+ * Returns compress types as an array for settings
43
+ *
44
+ * @return array
45
+ */
46
+ public function toArray()
47
+ {
48
+ $levels = $this->getCompressTypesArray();
49
+
50
+ foreach($levels as $key=>$value)
51
+ {
52
+ $array[$key] = Mage::helper('aitloadmon')->__(ucfirst($value));
53
+ }
54
+ return $array;
55
+ }
56
+ }
app/code/local/Aitoc/Aitloadmon/Model/Dblog.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Dblog extends Mage_Core_Model_Abstract
7
+ {
8
+ /**
9
+ * Standard model constructor
10
+ */
11
+ public function _construct()
12
+ {
13
+ parent::_construct();
14
+ $this->_init('aitloadmon/dblog');
15
+ }
16
+ }
app/code/local/Aitoc/Aitloadmon/Model/LevelSource.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_LevelSource
7
+ {
8
+
9
+ /**
10
+ * Returns load levels as a simple array
11
+ *
12
+ * @return array
13
+ */
14
+ public function getLoadArray()
15
+ {
16
+ return array(
17
+ Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_GREEN => 'green',
18
+ Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_YELLOW => 'yellow',
19
+ Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_RED => 'red',
20
+ Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_BLACK => 'black'
21
+ );
22
+ }
23
+
24
+
25
+ /**
26
+ * Returns load levels as an array for settings
27
+ *
28
+ * @return array
29
+ */
30
+ public function toOptionArray()
31
+ {
32
+ $array = array(
33
+ array('value' => 0, 'label'=>Mage::helper('aitloadmon')->__('No level')),
34
+ );
35
+
36
+ $levels = $this->getLoadArray();
37
+
38
+ foreach($levels as $key=>$value)
39
+ {
40
+ $array[] = array('value' => $key, 'label'=>Mage::helper('aitloadmon')->__(ucfirst($value)));
41
+ }
42
+
43
+ return $array;
44
+ }
45
+
46
+ /**
47
+ * Returns load levels as an array for settings
48
+ *
49
+ * @return array
50
+ */
51
+ public function toArray()
52
+ {
53
+ $array = array(
54
+ 0 => Mage::helper('aitloadmon')->__('No level'),
55
+ );
56
+
57
+ $levels = $this->getLoadArray();
58
+
59
+ foreach($levels as $key=>$value)
60
+ {
61
+ $array[$key] = Mage::helper('aitloadmon')->__(ucfirst($value));
62
+ }
63
+
64
+ return $array;
65
+ }
66
+ }
app/code/local/Aitoc/Aitloadmon/Model/Manage.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Manage extends Mage_Core_Controller_Varien_Front
7
+ {
8
+ private $_settingsKey = 'system/aitloadmon/manage';
9
+
10
+ /**
11
+ * Gets the setting of the module
12
+ *
13
+ * @return mixed
14
+ */
15
+ public function getSettings()
16
+ {
17
+ return unserialize(Mage::getStoreConfig($this->_settingsKey));
18
+ }
19
+
20
+ /**
21
+ * Sets the settings of the module
22
+ *
23
+ * @param $settings
24
+ */
25
+ public function setSettings($settings)
26
+ {
27
+ $config = Mage::getConfig();
28
+ $config->saveConfig($this->_settingsKey, serialize($settings), 'default', 0)->reinit();
29
+ }
30
+
31
+ }
app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Aitloadmon.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon extends Mage_Core_Model_Mysql4_Abstract
7
+ {
8
+
9
+ private $_sysInfo;
10
+
11
+
12
+ /**
13
+ * Standard model constructor
14
+ */
15
+ public function _construct()
16
+ {
17
+ $this->_init('aitloadmon/aitloadmon', 'id');
18
+ }
19
+
20
+ /**
21
+ * Initiates the data compression based on settings
22
+ */
23
+ public function compress()
24
+ {
25
+
26
+ $settings = Mage::getModel('aitloadmon/manage')->getSettings();
27
+
28
+ if(!$settings['enabled'])
29
+ {
30
+ return;
31
+ }
32
+
33
+ //last day
34
+ $format = $this->_getFormatByCompressType($settings['day']);
35
+ $from = 'DATE_SUB( NOW( ) , INTERVAL 2 DAY )';
36
+ $to = 'DATE_SUB( NOW( ) , INTERVAL 1 DAY )';
37
+ $this->_compress($from, $to, $format);
38
+
39
+ //last month
40
+ $format = $this->_getFormatByCompressType($settings['month']);
41
+ $from = 'DATE_SUB( NOW( ) , INTERVAL 2 MONTH )';
42
+ $to = 'DATE_SUB( NOW( ) , INTERVAL 1 MONTH )';
43
+ $this->_compress($from, $to, $format);
44
+
45
+ //last year
46
+ $format = $this->_getFormatByCompressType($settings['year']);
47
+ $from = 'DATE_SUB( NOW( ) , INTERVAL 2 YEAR )';
48
+ $to = 'DATE_SUB( NOW( ) , INTERVAL 1 YEAR )';
49
+ $this->_compress($from, $to, $format);
50
+
51
+ }
52
+
53
+ /**
54
+ * Returns compress formats for the data compression
55
+ *
56
+ * @return array
57
+ */
58
+ private function _getCompressFormats()
59
+ {
60
+ return array(
61
+ 1 => '%y-%m-%d-%H',
62
+ 2 => '%y-%m-%d',
63
+ 3 => '%y-%m',
64
+ );
65
+ }
66
+
67
+
68
+ /**
69
+ * Gets the compress format by the compress type
70
+ *
71
+ * @param $id
72
+ * @return mixed
73
+ */
74
+ private function _getFormatByCompressType($id)
75
+ {
76
+ $formats = $this->_getCompressFormats();
77
+ if(isset($formats[$id]))
78
+ {
79
+ return $formats[$id];
80
+ }
81
+ else
82
+ {
83
+ return $formats[1];
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Initiates manual compression
89
+ *
90
+ * @param string $from
91
+ * @param string $to
92
+ * @param int $formatId
93
+ */
94
+ public function manualCompress($from, $to, $formatId)
95
+ {
96
+ $this->_compress('\''.$from.'\'', '\''.$to.'\'', $this->_getFormatByCompressType($formatId));
97
+ }
98
+
99
+ /**
100
+ * Gets the system table info
101
+ *
102
+ * @return mixed
103
+ */
104
+ private function _getSysInfo()
105
+ {
106
+ if(!isset($this->_sysInfo))
107
+ {
108
+ $read = Mage::getSingleton('core/resource')->getConnection('core_read');
109
+ $query = 'SHOW TABLE STATUS WHERE Name = \''.$this->getTable('aitloadmon/aitloadmon').'\'';
110
+ $this->_sysInfo = $read->fetchRow($query);
111
+ }
112
+
113
+ return $this->_sysInfo;
114
+ }
115
+
116
+ /**
117
+ * Gets the number of rows of the table
118
+ *
119
+ * @return int
120
+ */
121
+ public function getRows()
122
+ {
123
+ $data = $this->_getSysInfo();
124
+ return isset($data['Rows'])?$data['Rows']:0;
125
+ }
126
+
127
+ /**
128
+ * Gets the size of the table
129
+ *
130
+ * @return int
131
+ */
132
+ public function getWeight()
133
+ {
134
+ $data = $this->_getSysInfo();
135
+ return isset($data['Data_length'])?$data['Data_length']:0;
136
+ }
137
+
138
+ /**
139
+ * Compresses data in the database
140
+ *
141
+ * @param string $from
142
+ * @param string $to
143
+ * @param string $format
144
+ */
145
+ private function _compress($from, $to, $format)
146
+ {
147
+ $table = $this->getTable('aitloadmon');
148
+
149
+ $read = Mage::getSingleton('core/resource')->getConnection('core_read');
150
+ $write = $read = Mage::getSingleton('core/resource')->getConnection('core_write');
151
+
152
+ $where = 'measure_time>='.$from.' AND measure_time<='.$to;
153
+
154
+ $readIdsQuery = 'SELECT id FROM '.$table.' WHERE '.$where;
155
+ $ids = $read->fetchCol($readIdsQuery);
156
+ if(!empty($ids))
157
+ {
158
+ $insertQuery ='INSERT INTO '.$table.' (measure_time, load_time_avg, load_time_max, page_group_id, page_views, max_page_views_per_minute)
159
+ SELECT MAX( measure_time ) AS measure_time, AVG( load_time_avg ) AS load_time_avg, MAX( load_time_max ) AS load_time_max, page_group_id, SUM( page_views ) AS page_views, MAX( max_page_views_per_minute ) AS max_page_views_per_minute
160
+ FROM '.$table.'
161
+ WHERE '.$where.'
162
+ GROUP BY DATE_FORMAT( `measure_time` , \''.$format.'\' ) , page_group_id';
163
+ $write->query($insertQuery);
164
+
165
+ $deleteQuery = 'DELETE FROM '.$table.' WHERE id IN('.implode(',',$ids).')';
166
+ $write->query($deleteQuery);
167
+ }
168
+ }
169
+
170
+ }
app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Aitloadmon/Collection.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
7
+ {
8
+ /**
9
+ * Standard constructor
10
+ */
11
+ public function _construct()
12
+ {
13
+ $this->_init('aitloadmon/aitloadmon');
14
+ }
15
+
16
+ /**
17
+ * Returns a string for GROUP BY in SQL
18
+ *
19
+ * @param string $startDate
20
+ * @param string $endDate
21
+ * @return bool|string
22
+ */
23
+ private function _getGroupString($startDate, $endDate)
24
+ {
25
+ $diff = strtotime($endDate) - strtotime($startDate);
26
+ switch($diff)
27
+ {
28
+ case ($diff>30*24*3600):
29
+ $format = '%y-%m';
30
+ break;
31
+ case ($diff>24*3600):
32
+ $format = '%y-%m-%d';
33
+ break;
34
+ case ($diff>3600):
35
+ $format = '%y-%m-%d-%H';
36
+ break;
37
+ default:
38
+ return false;
39
+ //$format = '%y-%m-%d-%H-%i';
40
+ break;
41
+ }
42
+
43
+ return 'DATE_FORMAT (`measure_time`, "'.$format.'"), page_group_id';
44
+ }
45
+
46
+ /**
47
+ * Gets a data collection bounded by 2 dates
48
+ *
49
+ * @param string $startDate
50
+ * @param string $endDate
51
+ * @return Aitoc_Aitloadmon_Model_Mysql4_Aitloadmon_Collection
52
+ */
53
+ public function filterByDates($startDate, $endDate)
54
+ {
55
+ if($groupString = $this->_getGroupString($startDate, $endDate))
56
+ {
57
+ $this->getSelect()->group($groupString)
58
+ ->reset(Zend_Db_Select::COLUMNS)
59
+ ->columns('measure_time, page_group_id, AVG(load_time_avg) AS load_time_avg, MAX(load_time_max) AS load_time_max, SUM(page_views) AS page_views');
60
+ }
61
+
62
+ $this->getSelect()->where('measure_time >=?',$startDate)
63
+ ->where('measure_time <=?',$endDate)
64
+ ->order('measure_time');
65
+ return $this;
66
+ }
67
+ }
app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Calculator.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Mysql4_Calculator extends Mage_Core_Model_Mysql4_Abstract
7
+ {
8
+ /**
9
+ * Standard model constructor
10
+ */
11
+ public function _construct()
12
+ {
13
+ $this->_init('aitloadmon/calculator', 'id');
14
+ }
15
+
16
+ /**
17
+ * Deletes everything from the table
18
+ */
19
+ public function deleteAll()
20
+ {
21
+ $adapter = $this->_getWriteAdapter();
22
+ $sql = 'DELETE FROM '.$this->getTable('aitloadmon/calculator');
23
+ $adapter->query($sql);
24
+ }
25
+ }
app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Calculator/Collection.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Mysql4_Calculator_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
7
+ {
8
+ /**
9
+ * Standard constructor
10
+ */
11
+ public function _construct()
12
+ {
13
+ $this->_init('aitloadmon/calculator');
14
+ }
15
+ }
app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Dblog.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Mysql4_Dblog extends Mage_Core_Model_Mysql4_Abstract
7
+ {
8
+
9
+ /**
10
+ * Standard model constructor
11
+ */
12
+ public function _construct()
13
+ {
14
+ $this->_init('aitloadmon/dblog', 'url_id');
15
+ }
16
+
17
+ }
app/code/local/Aitoc/Aitloadmon/Model/Mysql4/Dblog/Collection.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Mysql4_Dblog_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
7
+ {
8
+ /**
9
+ * Standard constructor
10
+ */
11
+ public function _construct()
12
+ {
13
+ $this->_init('aitloadmon/dblog');
14
+ }
15
+ }
app/code/local/Aitoc/Aitloadmon/Model/Mysql4/UrlRewrite.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Mysql4_UrlRewrite extends Mage_Core_Model_Mysql4_Url_Rewrite
7
+ {
8
+ /**
9
+ * Load rewrite information for request
10
+ * If $path is array - we must load all possible records and choose one matching earlier record in array
11
+ *
12
+ * @param Mage_Core_Model_Url_Rewrite $object
13
+ * @param array|string $path
14
+ * @return Mage_Core_Model_Resource_Url_Rewrite
15
+ */
16
+ public function loadByRequestPath(Mage_Core_Model_Url_Rewrite $object, $path)
17
+ {
18
+ if (!is_array($path)) {
19
+ $path = array($path);
20
+ }
21
+
22
+ $pathBind = array();
23
+ foreach ($path as $key => $url) {
24
+ $pathBind['path' . $key] = $url;
25
+ }
26
+ // Form select
27
+ $adapter = $this->_getReadAdapter();
28
+ $select = $adapter->select()
29
+ ->from($this->getMainTable())
30
+ ->where('request_path IN (:' . implode(', :', array_flip($pathBind)) . ')');
31
+ //->where('store_id IN(?)', array(Mage_Core_Model_App::ADMIN_STORE_ID, (int)$object->getStoreId()));
32
+
33
+ $items = $adapter->fetchAll($select, $pathBind);
34
+
35
+ // Go through all found records and choose one with lowest penalty - earlier path in array, concrete store
36
+ $mapPenalty = array_flip(array_values($path)); // we got mapping array(path => index), lower index - better
37
+ $currentPenalty = null;
38
+ $foundItem = null;
39
+ foreach ($items as $item) {
40
+ if (!array_key_exists($item['request_path'], $mapPenalty)) {
41
+ continue;
42
+ }
43
+ $penalty = $mapPenalty[$item['request_path']] << 1 + ($item['store_id'] ? 0 : 1);
44
+ if (!$foundItem || $currentPenalty > $penalty) {
45
+ $foundItem = $item;
46
+ $currentPenalty = $penalty;
47
+ if (!$currentPenalty) {
48
+ break; // Found best matching item with zero penalty, no reason to continue
49
+ }
50
+ }
51
+ }
52
+
53
+ // Set data and finish loading
54
+ if ($foundItem) {
55
+ $object->setData($foundItem);
56
+ }
57
+
58
+ // Finish
59
+ //$this->unserializeFields($object);
60
+ $this->_afterLoad($object);
61
+
62
+ return $this;
63
+ }
64
+ }
app/code/local/Aitoc/Aitloadmon/Model/Observer.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Observer
7
+ {
8
+ protected $_systemKey = 'system/aitloadmon/sent_info_date';
9
+ protected $_expire = 604799;
10
+
11
+ /**
12
+ * Initiates the data processing job
13
+ */
14
+ public function processData()
15
+ {
16
+ Mage::getModel('aitloadmon/process')->processData();
17
+ }
18
+
19
+ /**
20
+ * Initiates the data compressing job
21
+ */
22
+ public function compressData()
23
+ {
24
+ Mage::getModel('aitloadmon/aitloadmon')->getResource()->compress();
25
+ }
26
+
27
+ /**
28
+ * Sends the info to Server Calculator by cron
29
+ */
30
+ public function sendToAitoc()
31
+ {
32
+ $date = Mage::getStoreConfig($this->_systemKey);
33
+ $expire = $this->_expire;
34
+ if(!$date)
35
+ {
36
+ Mage::getConfig()
37
+ ->saveConfig($this->_systemKey, time(), 'default', 0)
38
+ ->reinit();
39
+ }
40
+ elseif((time() - $date) >= $expire)
41
+ {
42
+ Mage::getModel('aitloadmon/calculator')->sendCalcInfo();
43
+ Mage::getConfig()
44
+ ->saveConfig($this->_systemKey, time(), 'default', 0)
45
+ ->reinit();
46
+ }
47
+ }
48
+ }
app/code/local/Aitoc/Aitloadmon/Model/Process.php ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Process extends Mage_Core_Controller_Varien_Front
7
+ {
8
+ /**
9
+ * Collects Magento routes
10
+ *
11
+ * @return Aitoc_Aitloadmon_Model_Process
12
+ */
13
+ protected function _init()
14
+ {
15
+ $router = Mage::getModel('aitloadmon/router');
16
+ $router->collectRoutes('frontend', 'standard');
17
+ $this->addRouter('custom', $router);
18
+
19
+ $default = new Mage_Core_Controller_Varien_Router_Default();
20
+ $this->addRouter('default', $default);
21
+
22
+ return $this;
23
+ }
24
+
25
+ /**
26
+ * Pre-processes data for saving
27
+ *
28
+ * @param array $data
29
+ * @return array
30
+ */
31
+ private function _prepareForSave($data)
32
+ {
33
+ $result = array();
34
+ foreach($data as $data_item)
35
+ {
36
+ if(isset($data_item['request_uri']) && isset($data_item['start']) && $data_item['start'] && isset($data_item['end']) && $data_item['end'])
37
+ {
38
+ if($groupId = $this->_getGroupIdByUri('http://some.host'.$data_item['request_uri']))
39
+ {
40
+ if(!isset($result[$groupId]))
41
+ {
42
+ $result[$groupId] = array('sum'=>0,'max'=>0,'cnt'=>0);
43
+ }
44
+ $loadTime = (float)$data_item['end'] - (float)$data_item['start'];
45
+ $result[$groupId]['sum'] += $loadTime;
46
+ $result[$groupId]['end'] = $data_item['end'];
47
+ if($loadTime > $result[$groupId]['max'])
48
+ {
49
+ $result[$groupId]['max'] = $loadTime;
50
+ }
51
+ $result[$groupId]['cnt']++;
52
+ }
53
+ }
54
+
55
+ }
56
+ return $result;
57
+ }
58
+
59
+ /**
60
+ * Saves the processed data to database
61
+ *
62
+ * @param array $result
63
+ * @return float|int
64
+ */
65
+ private function _saveData($result)
66
+ {
67
+ $model = Mage::getModel('aitloadmon/aitloadmon');
68
+ $calcModel = Mage::getModel('aitloadmon/calculator');
69
+ $calcModel->load(0);
70
+ $maxAvgLoadTime = 0;
71
+ $maxPageViewsPerMinute = 0;
72
+ $sumLoadTime = 0;
73
+ foreach($result as $groupId => $resultItem)
74
+ {
75
+ $avgLoadTime = $resultItem['sum']/$resultItem['cnt'];
76
+ if($avgLoadTime > $maxAvgLoadTime)
77
+ {
78
+ $maxAvgLoadTime = $avgLoadTime;
79
+ }
80
+ $model->load(0);
81
+ $model->setData(array(
82
+
83
+ 'measure_time' => date('Y-m-d H:i:s',ceil($resultItem['end']/60)*60),
84
+ 'load_time_avg' => $avgLoadTime,
85
+ 'load_time_max' => $resultItem['max'],
86
+ 'page_group_id' => $groupId,
87
+ 'page_views' => $resultItem['cnt'],
88
+ 'max_page_views_per_minute' => $resultItem['cnt'],
89
+
90
+ ));
91
+ $model->save();
92
+
93
+ $calcModel->addData(array(
94
+ 'pg'.$groupId.'_avg_load' => $avgLoadTime,
95
+ 'pg'.$groupId.'_views' => $resultItem['cnt'],
96
+ ));
97
+
98
+ $sumLoadTime += $resultItem['sum'];
99
+ $maxPageViewsPerMinute += $resultItem['cnt'];
100
+ }
101
+
102
+ if($maxPageViewsPerMinute)
103
+ {
104
+ $calcModel->addData(array(
105
+ 'load_time_avg' => $sumLoadTime/$maxPageViewsPerMinute,
106
+ 'max_page_views_per_minute' => $maxPageViewsPerMinute,
107
+ ));
108
+ $calcModel->save();
109
+ }
110
+ return $maxAvgLoadTime;
111
+ }
112
+
113
+ /**
114
+ * Returns a load level based on load time
115
+ *
116
+ * @param float|int $maxAvgLoadTime
117
+ * @return int
118
+ */
119
+ private function _getLoadLevelByLoad($maxAvgLoadTime)
120
+ {
121
+ $yellow = Mage::getStoreConfig('system/aitloadmon/load_yellow');
122
+ $red = Mage::getStoreConfig('system/aitloadmon/load_red');
123
+ $black = Mage::getStoreConfig('system/aitloadmon/load_black');
124
+ if($maxAvgLoadTime > $black)
125
+ {
126
+ $loadLevel = Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_BLACK;
127
+ }
128
+ elseif($maxAvgLoadTime > $red)
129
+ {
130
+ $loadLevel = Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_RED;
131
+ }
132
+ elseif($maxAvgLoadTime > $yellow)
133
+ {
134
+ $loadLevel = Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_YELLOW;
135
+ }
136
+ else
137
+ {
138
+ $loadLevel = Aitoc_Aitloadmon_Model_Aitloadmon::LOAD_LEVEL_GREEN;
139
+ }
140
+ return $loadLevel;
141
+ }
142
+
143
+ /**
144
+ * Splits the data by minutes
145
+ *
146
+ * @param $data
147
+ * @return array
148
+ */
149
+ private function _getTimedData($data)
150
+ {
151
+ $return = array();
152
+ foreach($data as $dataItem)
153
+ {
154
+ if(isset($dataItem['end']))
155
+ {
156
+ $minute = ceil($dataItem['end']/60);
157
+ if(!isset($dataItem['end']))
158
+ {
159
+ continue;
160
+ }
161
+ if(!isset($return[$minute]))
162
+ {
163
+ $return[$minute] = array();
164
+ }
165
+ $return[$minute][] = $dataItem;
166
+ }
167
+ }
168
+
169
+ return $return;
170
+ }
171
+
172
+ /**
173
+ * Processes the data collected earlier
174
+ *
175
+ * @return array
176
+ */
177
+ public function processData()
178
+ {
179
+
180
+ $this->_init();
181
+ $rawData = Aitoc_Aitloadmon_Collect::getData();
182
+ $maxAvgLoadTime = 0;
183
+
184
+ $timedData = $this->_getTimedData($rawData);
185
+
186
+ foreach($timedData as $data)
187
+ {
188
+ $result = $this->_prepareForSave($data);
189
+ $maxAvgLoadTimeTemp = $this->_saveData($result);
190
+ $maxAvgLoadTime = ($maxAvgLoadTime<$maxAvgLoadTimeTemp)?$maxAvgLoadTimeTemp:$maxAvgLoadTime;
191
+ }
192
+
193
+ $currentLoadLevel = $this->_getLoadLevelByLoad($maxAvgLoadTime);
194
+ $oldLoadLevel = Mage::helper('aitloadmon')->getLoadLevel();
195
+ if($currentLoadLevel != $oldLoadLevel)
196
+ {
197
+ Mage::helper('aitloadmon')->setLoadLevel($currentLoadLevel);
198
+ Mage::dispatchEvent('aitloadmon_load_level_changed',array('from'=>$oldLoadLevel,'to'=>$currentLoadLevel));
199
+ }
200
+ return isset($result)?$result:array();
201
+ }
202
+
203
+ private function _getUrlCuts()
204
+ {
205
+ if(!isset($this->_urlCuts))
206
+ {
207
+ $stores = Mage::getModel('core/store')->getCollection();
208
+ $storeUrls = array();
209
+ $urlCuts = array();
210
+ foreach($stores as $store)
211
+ {
212
+ $id = $store->getId();
213
+ $storeUrls[] = Mage::getStoreConfig('web/unsecure/base_url',$id);
214
+ $storeUrls[] = Mage::getStoreConfig('web/secure/base_url',$id);
215
+ $storeUrls[] = Mage::getStoreConfig('web/unsecure/base_link_url',$id);
216
+ $storeUrls[] = Mage::getStoreConfig('web/secure/base_link_url',$id);
217
+ }
218
+ foreach(array_unique($storeUrls) as $url)
219
+ {
220
+ $parsedUrl = parse_url($url);
221
+ if(isset($parsedUrl['path']))
222
+ {
223
+ $url = ltrim($parsedUrl['path'],'/');
224
+ if($url)
225
+ {
226
+ $urlCuts[] = $url;
227
+ }
228
+ }
229
+ }
230
+ $this->_urlCuts = $urlCuts;
231
+ }
232
+ return $this->_urlCuts;
233
+ }
234
+
235
+
236
+ /**
237
+ * Gets page view group based on its request uri
238
+ *
239
+ * @param string $uri
240
+ * @return int|bool
241
+ */
242
+ private function _getGroupIdByUri($uri)
243
+ {
244
+ $urlCuts = $this->_getUrlCuts();
245
+ foreach($urlCuts as $urlCut)
246
+ {
247
+ $uri = str_replace($urlCut,'',$uri);
248
+ }
249
+ $uri = str_replace('index.php/','',$uri);
250
+ $request = new Mage_Core_Controller_Request_Http($uri);
251
+ //commented if for 1401 support
252
+ //if (!$request->isStraight()) {
253
+ Mage::getModel('aitloadmon/urlRewrite')->rewrite($request);
254
+ //}
255
+ $this->_rewriteRequest($request);
256
+
257
+ foreach ($this->_routers as $router) {
258
+ if ($router->match($request)) {
259
+ break;
260
+ }
261
+ }
262
+ $module = $request->getModuleName();
263
+ $groupIds = Mage::helper('aitloadmon')->getGroupsArray();
264
+ if($module == 'admin')
265
+ {
266
+ return false;
267
+ }
268
+ elseif(isset($groupIds[$module]))
269
+ {
270
+ return $groupIds[$module];
271
+ }
272
+ else
273
+ {
274
+ return 1;
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Applies rewrites to the request object
280
+ *
281
+ * @param $request
282
+ */
283
+ private function _rewriteRequest($request)
284
+ {
285
+ $config = Mage::getConfig()->getNode('global/rewrite');
286
+ if (!$config) {
287
+ return;
288
+ }
289
+ foreach ($config->children() as $rewrite) {
290
+ $from = (string)$rewrite->from;
291
+ $to = (string)$rewrite->to;
292
+ if (empty($from) || empty($to)) {
293
+ continue;
294
+ }
295
+ $from = $this->_processRewriteUrl($from);
296
+ $to = $this->_processRewriteUrl($to);
297
+
298
+ $pathInfo = preg_replace($from, $to, $request->getPathInfo());
299
+
300
+ if (isset($rewrite->complete)) {
301
+ $request->setPathInfo($pathInfo);
302
+ } else {
303
+ $request->rewritePathInfo($pathInfo);
304
+ }
305
+ }
306
+
307
+ }
308
+
309
+ }
app/code/local/Aitoc/Aitloadmon/Model/Router.php ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_Router extends Mage_Core_Controller_Varien_Router_Standard
7
+ {
8
+ /**
9
+ * Dispatches the request until the needed module is found and sets the module params to the request object.
10
+ * Is needed to detect the requested page view group by its module name
11
+ *
12
+ * This is mainly a copy paste from Magento core, but it does not execute the routed action
13
+ *
14
+ * @param Zend_Controller_Request_Http $request
15
+ * @return bool
16
+ */
17
+
18
+ public function match(Zend_Controller_Request_Http $request)
19
+ {
20
+ //checking before even try to find out that current module
21
+ //should use this router
22
+ #if (!$this->_beforeModuleMatch()) {
23
+ # return false;
24
+ #}
25
+
26
+ $this->fetchDefault();
27
+
28
+ $front = $this->getFront();
29
+ $path = trim($request->getPathInfo(), '/');
30
+
31
+ if ($path) {
32
+ $p = explode('/', $path);
33
+ } else {
34
+ $p = explode('/', Mage::getStoreConfig('web/default/front'));
35
+ }
36
+
37
+ // get module name
38
+ if ($request->getModuleName()) {
39
+ $module = $request->getModuleName();
40
+ } else {
41
+ if (!empty($p[0])) {
42
+ $module = $p[0];
43
+ } else {
44
+ $module = $this->getFront()->getDefault('module');
45
+ $request->setAlias(Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS, '');
46
+ }
47
+ }
48
+ if (!$module) {
49
+ if (Mage::app()->getStore()->isAdmin()) {
50
+ $module = 'admin';
51
+ } else {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Searching router args by module name from route using it as key
58
+ */
59
+ $modules = $this->getModuleByFrontName($module);
60
+
61
+ if ($modules === false) {
62
+ return false;
63
+ }
64
+
65
+ // checks after we found out that this router should be used for current module
66
+ #if (!$this->_afterModuleMatch()) {
67
+ # return false;
68
+ #}
69
+
70
+ /**
71
+ * Going through modules to find appropriate controller
72
+ */
73
+ $found = false;
74
+ foreach ($modules as $realModule) {
75
+ $request->setRouteName($this->getRouteByFrontName($module));
76
+
77
+ // get controller name
78
+ if ($request->getControllerName()) {
79
+ $controller = $request->getControllerName();
80
+ } else {
81
+ if (!empty($p[1])) {
82
+ $controller = $p[1];
83
+ } else {
84
+ $controller = $front->getDefault('controller');
85
+ $request->setAlias(
86
+ Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS,
87
+ ltrim($request->getOriginalPathInfo(), '/')
88
+ );
89
+ }
90
+ }
91
+
92
+ // get action name
93
+ if (empty($action)) {
94
+ if ($request->getActionName()) {
95
+ $action = $request->getActionName();
96
+ } else {
97
+ $action = !empty($p[2]) ? $p[2] : $front->getDefault('action');
98
+ }
99
+ }
100
+
101
+ //checking if this place should be secure
102
+ $this->_checkShouldBeSecure($request, '/'.$module.'/'.$controller.'/'.$action);
103
+
104
+ $controllerClassName = $this->_validateControllerClassName($realModule, $controller);
105
+ if (!$controllerClassName) {
106
+ continue;
107
+ }
108
+
109
+ // instantiate controller class
110
+ $controllerInstance = Mage::getControllerInstance($controllerClassName, $request, $front->getResponse());
111
+
112
+ if (!$controllerInstance->hasAction($action)) {
113
+ continue;
114
+ }
115
+
116
+ $found = true;
117
+ break;
118
+ }
119
+
120
+ /**
121
+ * if we did not found any suitable
122
+ */
123
+ if (!$found) {
124
+ if ($this->_noRouteShouldBeApplied()) {
125
+ $controller = 'index';
126
+ $action = 'noroute';
127
+
128
+ $controllerClassName = $this->_validateControllerClassName($realModule, $controller);
129
+ if (!$controllerClassName) {
130
+ return false;
131
+ }
132
+
133
+ // instantiate controller class
134
+ $controllerInstance = Mage::getControllerInstance($controllerClassName, $request,
135
+ $front->getResponse());
136
+
137
+ if (!$controllerInstance->hasAction($action)) {
138
+ return false;
139
+ }
140
+ } else {
141
+ return false;
142
+ }
143
+ }
144
+
145
+ // set values only after all the checks are done
146
+ $request->setModuleName($module);
147
+ $request->setControllerName($controller);
148
+ $request->setActionName($action);
149
+ $request->setControllerModule($realModule);
150
+
151
+ // set parameters from pathinfo
152
+ #for ($i = 3, $l = sizeof($p); $i < $l; $i += 2) {
153
+ # $request->setParam($p[$i], isset($p[$i+1]) ? urldecode($p[$i+1]) : '');
154
+ #}
155
+
156
+ // dispatch action
157
+ #$request->setDispatched(true);
158
+ #$controllerInstance->dispatch($action);
159
+
160
+ return true;
161
+ }
162
+ }
app/code/local/Aitoc/Aitloadmon/Model/SysInfo.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_SysInfo extends Aitoc_Aitloadmon_Model_System_Abstract
7
+ {
8
+ protected $_systemUrl = 'http://www.aitoc.com/configuration-calculator';
9
+ protected $_systemKey = 'system/aitloadmon/system_info';
10
+ protected $_systemDateKey = 'system/aitloadmon/system_info_date';
11
+ protected $_expire = 86400;
12
+ }
app/code/local/Aitoc/Aitloadmon/Model/System.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_System extends Aitoc_Aitloadmon_Model_System_Abstract
7
+ {
8
+ protected $_systemUrl = 'http://www.aitoc.com/monitor-module-news';
9
+ protected $_systemKey = 'system/aitloadmon/system';
10
+ protected $_systemDateKey = 'system/aitloadmon/system_date';
11
+ protected $_expire = 86400;
12
+ }
app/code/local/Aitoc/Aitloadmon/Model/System/Abstract.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ abstract class Aitoc_Aitloadmon_Model_System_Abstract
7
+ {
8
+ public function getSystem()
9
+ {
10
+ $system_body = Mage::getStoreConfig($this->_systemKey);
11
+ $date = Mage::getStoreConfig($this->_systemDateKey);
12
+ if(!$system_body || (time()-$date)>$this->_expire)
13
+ {
14
+ $system = @file_get_contents($this->_systemUrl);
15
+ preg_match('/<body.*?>(.*)<\/body>/msi',$system,$matches);
16
+ $system_body = '';
17
+ if(isset($matches[1]))
18
+ {
19
+ $system_body = $matches[1];
20
+ }
21
+ Mage::getConfig()
22
+ ->saveConfig($this->_systemKey, $system_body, 'default', 0)
23
+ ->saveConfig($this->_systemDateKey, time(), 'default', 0)
24
+ ->reinit();
25
+ }
26
+ return $system_body;
27
+ }
28
+
29
+ public function getSystemUrl()
30
+ {
31
+ return $this->_systemUrl;
32
+ }
33
+ }
app/code/local/Aitoc/Aitloadmon/Model/UrlRewrite.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Model_UrlRewrite extends Mage_Core_Model_Url_Rewrite
7
+ {
8
+ /**
9
+ * Standard model constructor
10
+ */
11
+ protected function _construct()
12
+ {
13
+ $this->_init('aitloadmon/urlRewrite');
14
+ }
15
+
16
+ public function loadByRequestPath($path)
17
+ {
18
+ $this->setId(null);
19
+ $this->_getResource()->loadByRequestPath($this, $path);
20
+ $this->_afterLoad();
21
+ $this->setOrigData();
22
+ $this->_hasDataChanges = false;
23
+ return $this;
24
+ }
25
+ }
app/code/local/Aitoc/Aitloadmon/controllers/Adminhtml/AitloadmonController.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Adminhtml_AitloadmonController extends Mage_Adminhtml_Controller_Action
7
+ {
8
+ /**
9
+ * Sets active menu, breadcrumbs and title
10
+ *
11
+ * @return Aitoc_Aitloadmon_Adminhtml_AitloadmonController
12
+ */
13
+ protected function _initIndexAction()
14
+ {
15
+ if(!class_exists('Aitoc_Aitloadmon_Collect',false))
16
+ {
17
+ Mage::getSingleton('core/session')->addError(Mage::helper('aitloadmon')->__('Please add the following line to your index.php file(s):<br>include_once(dirname($_SERVER[\'SCRIPT_FILENAME\']).DIRECTORY_SEPARATOR.\'lib\'.DIRECTORY_SEPARATOR.\'Aitoc\'.DIRECTORY_SEPARATOR.\'Aitloadmon\'.DIRECTORY_SEPARATOR.\'Collect.php\');new Aitoc_Aitloadmon_Collect();'));
18
+ }
19
+ $this->loadLayout()
20
+ ->_setActiveMenu('system/aitloadmon')
21
+ ->_addBreadcrumb(Mage::helper('aitloadmon')->__('View Stats'), Mage::helper('aitloadmon')->__('View Stats'));
22
+ $this->_title(Mage::helper('aitloadmon')->__('Server Load Monitor'))->_title(Mage::helper('aitloadmon')->__('View Stats'));
23
+ return $this;
24
+ }
25
+
26
+ /**
27
+ * Sets active menu, breadcrumbs and title
28
+ *
29
+ * @return Aitoc_Aitloadmon_Adminhtml_AitloadmonController
30
+ */
31
+ protected function _initManageAction()
32
+ {
33
+ $this->loadLayout()
34
+ ->_setActiveMenu('system/aitloadmon')
35
+ ->_addBreadcrumb(Mage::helper('aitloadmon')->__('Manage Data'), Mage::helper('aitloadmon')->__('Manage Data'));
36
+ $this->_title(Mage::helper('aitloadmon')->__('Server Load Monitor'))->_title(Mage::helper('aitloadmon')->__('Manage Data'));
37
+ return $this;
38
+ }
39
+
40
+ /**
41
+ * Processes request and sets its data to the main block
42
+ */
43
+ public function indexAction() {
44
+ $this->_initIndexAction();
45
+
46
+ if(!$startDate = $this->getRequest()->getParam('startDate'))
47
+ {
48
+ $startDate = date('Y-m-d H:i:s', time()-3600+1);
49
+ }
50
+
51
+ if(!$endDate = $this->getRequest()->getParam('endDate'))
52
+ {
53
+ $endDate = date('Y-m-d H:i:s');
54
+ }
55
+
56
+ $type = $this->getRequest()->getParam('type');
57
+ if(!$type)
58
+ {
59
+ $type = 'max';
60
+ }
61
+
62
+ $this->getLayout()->getBlock('main_bl')
63
+ ->setType($type)
64
+ ->setStartDate($startDate)
65
+ ->setEndDate($endDate);
66
+
67
+ $compare = $this->getRequest()->getParam('compare');
68
+
69
+ if($compare && !$startDateCompare = $this->getRequest()->getParam('startDateCompare'))
70
+ {
71
+ $startDateCompare = date('Y-m-d H:i:s', time()-86400);
72
+ }
73
+
74
+ if($compare)
75
+ {
76
+ $endDateCompare = date('Y-m-d H:i:s', strtotime($startDateCompare) + (strtotime($endDate) - strtotime($startDate)));
77
+ $this->getLayout()->getBlock('main_bl')->setCompare($compare)
78
+ ->setStartDateCompare($startDateCompare)
79
+ ->setEndDateCompare($endDateCompare);
80
+ }
81
+
82
+ $this->renderLayout();
83
+ }
84
+
85
+ /**
86
+ * This action is used to display the manage data blocks and forms
87
+ */
88
+ public function manageAction() {
89
+ if(Mage::app()->getRequest()->getParam('submit_auto'))
90
+ {
91
+ $data = array(
92
+ 'enabled' => Mage::app()->getRequest()->getParam('auto_enable'),
93
+ 'year' => Mage::app()->getRequest()->getParam('auto_year'),
94
+ 'month' => Mage::app()->getRequest()->getParam('auto_month'),
95
+ 'day' => Mage::app()->getRequest()->getParam('auto_day'),
96
+ );
97
+ Mage::getModel('aitloadmon/manage')->setSettings($data);
98
+ Mage::getSingleton('core/session')->addSuccess(Mage::helper('aitloadmon')->__('The settings are successfully saved.'));
99
+ }
100
+ elseif(Mage::app()->getRequest()->getParam('submit_manual'))
101
+ {
102
+ Mage::getModel('aitloadmon/aitloadmon')->getResource()->manualCompress(Mage::app()->getRequest()->getParam('manual_from'), Mage::app()->getRequest()->getParam('manual_to'), Mage::app()->getRequest()->getParam('manual_compress'));
103
+ Mage::getSingleton('core/session')->addSuccess(Mage::helper('aitloadmon')->__('The data is successfully compressed.'));
104
+ }
105
+ $this->_initManageAction();
106
+ $this->renderLayout();
107
+ }
108
+ }
app/code/local/Aitoc/Aitloadmon/controllers/Adminhtml/CalculatorController.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Adminhtml_CalculatorController extends Mage_Adminhtml_Controller_Action
7
+ {
8
+ /**
9
+ * Opens the form for data source selection for Server Calculator
10
+ */
11
+ public function indexAction()
12
+ {
13
+ $this->loadLayout()
14
+ ->_setActiveMenu('system/aitloadmon')
15
+ ->_addBreadcrumb(Mage::helper('aitloadmon')->__('Info for Magento Server Configuration Calculator'), Mage::helper('aitloadmon')->__('Info for Magento Server Configuration Calculator'));
16
+ $this->_title(Mage::helper('aitloadmon')->__('Server Load Monitor'))->_title(Mage::helper('aitloadmon')->__('Info for Magento Server Configuration Calculator'));
17
+ $this->renderLayout();
18
+ }
19
+
20
+ /**
21
+ * Displays the result to enter to Server Calculator
22
+ */
23
+ public function submitAction()
24
+ {
25
+ $this->loadLayout()
26
+ ->_setActiveMenu('system/aitloadmon')
27
+ ->_addBreadcrumb(Mage::helper('aitloadmon')->__('Info for Magento Server Configuration Calculator'), Mage::helper('aitloadmon')->__('Info for Server Calculator'));
28
+ $this->_title(Mage::helper('aitloadmon')->__('Server Load Monitor'))->_title(Mage::helper('aitloadmon')->__('Info for Magento Server Configuration Calculator'));
29
+
30
+ $model = Mage::getModel('aitloadmon/calculator');
31
+ if(isset($_FILES['csv_file']['name']))
32
+ {
33
+ $model->setCsvFile($_FILES['csv_file']['tmp_name']);
34
+ }
35
+ $method = 'get'.ucfirst($this->getRequest()->getParam('source')).'Visitors';
36
+ $this->getLayout()->getBlock('aitload_calculator_result')
37
+ ->setProducts($model->getProductInfo())
38
+ ->setLoad($model->$method());
39
+ $this->renderLayout();
40
+ }
41
+
42
+ }
app/code/local/Aitoc/Aitloadmon/etc/adminhtml.xml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <menu>
4
+ <system>
5
+ <children>
6
+ <stats module="aitloadmon">
7
+ <title>Server Load Monitor</title>
8
+ <sort_order>2</sort_order>
9
+ <action>adminhtml/aitloadmon</action>
10
+ <children>
11
+ <stats translate="title" module="aitloadmon">
12
+ <title>View Stats</title>
13
+ <action>adminhtml/aitloadmon</action>
14
+ <sort_order>10</sort_order>
15
+ </stats>
16
+ <calc translate="title" module="aitloadmon">
17
+ <title>Info for Magento Server Configuration Calculator</title>
18
+ <action>adminhtml/calculator</action>
19
+ <sort_order>20</sort_order>
20
+ </calc>
21
+ </children>
22
+ </stats>
23
+ </children>
24
+ </system>
25
+ </menu>
26
+ <acl>
27
+ <resources>
28
+ <admin>
29
+ <children>
30
+ <aitloadmon>
31
+ <title>Server Load Monitor</title>
32
+ <sort_order>300</sort_order>
33
+ </aitloadmon>
34
+ </children>
35
+ </admin>
36
+ </resources>
37
+ </acl>
38
+ </config>
39
+ <!--
40
+ /**
41
+ * @copyright Copyright (c) 2013 AITOC, Inc.
42
+ * @license License agreement could be found at the root folder of this package
43
+ */
44
+ -->
app/code/local/Aitoc/Aitloadmon/etc/config.xml ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Aitoc_Aitloadmon>
5
+ <version>1.0.0</version>
6
+ </Aitoc_Aitloadmon>
7
+ </modules>
8
+ <admin>
9
+ <routers>
10
+ <adminhtml>
11
+ <args>
12
+ <modules>
13
+ <aitloadmon before="Mage_Adminhtml">Aitoc_Aitloadmon_Adminhtml</aitloadmon>
14
+ </modules>
15
+ </args>
16
+ </adminhtml>
17
+ </routers>
18
+ </admin>
19
+ <adminhtml>
20
+ <layout>
21
+ <updates>
22
+ <aitloadmon>
23
+ <file>aitloadmon.xml</file>
24
+ </aitloadmon>
25
+ </updates>
26
+ </layout>
27
+ </adminhtml>
28
+ <global>
29
+ <models>
30
+ <aitloadmon>
31
+ <class>Aitoc_Aitloadmon_Model</class>
32
+ <resourceModel>aitloadmon_mysql4</resourceModel>
33
+ </aitloadmon>
34
+ <aitloadmon_mysql4>
35
+ <class>Aitoc_Aitloadmon_Model_Mysql4</class>
36
+ <entities>
37
+ <aitloadmon>
38
+ <table>aitloadmon</table>
39
+ </aitloadmon>
40
+ <dblog>
41
+ <table>log_url</table>
42
+ </dblog>
43
+ <calculator>
44
+ <table>aitloadmon_calculator</table>
45
+ </calculator>
46
+ </entities>
47
+ </aitloadmon_mysql4>
48
+ </models>
49
+ <resources>
50
+ <aitloadmon_setup>
51
+ <setup>
52
+ <module>Aitoc_Aitloadmon</module>
53
+ </setup>
54
+ <connection>
55
+ <use>core_setup</use>
56
+ </connection>
57
+ </aitloadmon_setup>
58
+ <aitloadmon_write>
59
+ <connection>
60
+ <use>core_write</use>
61
+ </connection>
62
+ </aitloadmon_write>
63
+ <aitloadmon_read>
64
+ <connection>
65
+ <use>core_read</use>
66
+ </connection>
67
+ </aitloadmon_read>
68
+ </resources>
69
+ <blocks>
70
+ <aitloadmon>
71
+ <class>Aitoc_Aitloadmon_Block</class>
72
+ </aitloadmon>
73
+ </blocks>
74
+ <helpers>
75
+ <aitloadmon>
76
+ <class>Aitoc_Aitloadmon_Helper</class>
77
+ </aitloadmon>
78
+ </helpers>
79
+ </global>
80
+ <crontab>
81
+ <jobs>
82
+ <aitloadmon_process_data>
83
+ <schedule><cron_expr>*/5 * * * *</cron_expr></schedule>
84
+ <run><model>aitloadmon/observer::processData</model></run>
85
+ </aitloadmon_process_data>
86
+ <aitloadmon_compress_data>
87
+ <schedule><cron_expr>1 */1 * * *</cron_expr></schedule>
88
+ <run><model>aitloadmon/observer::compressData</model></run>
89
+ </aitloadmon_compress_data>
90
+ <aitloadmon_calc_send>
91
+ <schedule><cron_expr>1 4 * * *</cron_expr></schedule>
92
+ <run><model>aitloadmon/observer::sendToAitoc</model></run>
93
+ </aitloadmon_calc_send>
94
+ </jobs>
95
+ </crontab>
96
+ <default>
97
+ <system>
98
+ <aitloadmon>
99
+ <load_yellow>3</load_yellow>
100
+ <load_red>7</load_red>
101
+ <load_black>10</load_black>
102
+ <manage>a:4:{s:7:"enabled";i:1;s:4:"year";i:3;s:5:"month";i:2;s:3:"day";i:1;}</manage>
103
+ </aitloadmon>
104
+ </system>
105
+ </default>
106
+ </config>
107
+ <!--
108
+ /**
109
+ * @copyright Copyright (c) 2013 AITOC, Inc.
110
+ * @license License agreement could be found at the root folder of this package
111
+ */
112
+ -->
app/code/local/Aitoc/Aitloadmon/etc/system.xml ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <sections>
4
+ <system>
5
+ <groups>
6
+ <aitloadmon translate="label" module="aitloadmon">
7
+ <label>Server Load Monitor</label>
8
+ <frontend_type>text</frontend_type>
9
+ <sort_order>1010</sort_order>
10
+ <show_in_default>1</show_in_default>
11
+ <show_in_website>0</show_in_website>
12
+ <show_in_store>0</show_in_store>
13
+ <fields>
14
+ <load_yellow translate="label">
15
+ <label>Yellow load level after</label>
16
+ <comment><![CDATA[Yellow load level means average server load (sec); default value is 3 sec (Green load level will be if the response time will be less or equal 3 sec that means minor server load)]]></comment>
17
+ <frontend_type>text</frontend_type>
18
+ <validate>validate-number</validate>
19
+ <sort_order>500</sort_order>
20
+ <show_in_default>1</show_in_default>
21
+ <show_in_website>0</show_in_website>
22
+ <show_in_store>0</show_in_store>
23
+ </load_yellow>
24
+
25
+ <load_red translate="label">
26
+ <label>Red load level after</label>
27
+ <comment><![CDATA[Red load level means peak server load (sec); default value is 7 sec]]></comment>
28
+ <frontend_type>text</frontend_type>
29
+ <validate>validate-number</validate>
30
+ <sort_order>600</sort_order>
31
+ <show_in_default>1</show_in_default>
32
+ <show_in_website>0</show_in_website>
33
+ <show_in_store>0</show_in_store>
34
+ </load_red>
35
+
36
+
37
+ <load_black translate="label">
38
+ <label>Black load level after</label>
39
+ <comment><![CDATA[Black load level means critical load or server failure (sec); default value is 10 sec]]></comment>
40
+ <frontend_type>text</frontend_type>
41
+ <validate>validate-number</validate>
42
+ <sort_order>700</sort_order>
43
+ <show_in_default>1</show_in_default>
44
+ <show_in_website>0</show_in_website>
45
+ <show_in_store>0</show_in_store>
46
+ </load_black>
47
+
48
+ <manage_link translate="label">
49
+ <label>Manage data compression</label>
50
+ <frontend_model>aitloadmon/adminhtml_aitlink</frontend_model>
51
+ <sort_order>800</sort_order>
52
+ <show_in_default>1</show_in_default>
53
+ <show_in_website>0</show_in_website>
54
+ <show_in_store>0</show_in_store>
55
+ </manage_link>
56
+ </fields>
57
+ </aitloadmon>
58
+ </groups>
59
+ </system>
60
+ </sections>
61
+ </config>
62
+ <!--
63
+ /**
64
+ * @copyright Copyright (c) 2013 AITOC, Inc.
65
+ * @license License agreement could be found at the root folder of this package
66
+ */
67
+ -->
app/code/local/Aitoc/Aitloadmon/sql/aitloadmon_setup/mysql4-install-1.0.0.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ $installer = $this;
7
+
8
+ $installer->startSetup();
9
+
10
+ $installer->run("
11
+
12
+ -- DROP TABLE IF EXISTS {$this->getTable('aitloadmon')};
13
+ CREATE TABLE {$this->getTable('aitloadmon')} (
14
+ `id` int(11) NOT NULL AUTO_INCREMENT,
15
+ `measure_time` datetime NOT NULL,
16
+ `load_time_avg` float NOT NULL,
17
+ `load_time_max` float NOT NULL,
18
+ `page_group_id` int(11) NOT NULL,
19
+ `page_views` int(11) NOT NULL,
20
+ `max_page_views_per_minute` int(11) NOT NULL,
21
+ PRIMARY KEY (`id`),
22
+ KEY `measure_time` (`measure_time`)
23
+ ) ENGINE=MyISAM ;
24
+
25
+ ");
26
+
27
+ $installer->run("
28
+
29
+ -- DROP TABLE IF EXISTS {$this->getTable('aitloadmon/calculator')};
30
+ CREATE TABLE {$this->getTable('aitloadmon/calculator')} (
31
+ `id` int(11) NOT NULL AUTO_INCREMENT,
32
+ `load_time_avg` float NOT NULL,
33
+ `max_page_views_per_minute` int(11) NOT NULL,
34
+ `pg1_avg_load` float NOT NULL,
35
+ `pg1_views` int(11) NOT NULL,
36
+ `pg2_avg_load` float NOT NULL,
37
+ `pg2_views` int(11) NOT NULL,
38
+ `pg3_avg_load` float NOT NULL,
39
+ `pg3_views` int(11) NOT NULL,
40
+ `pg4_avg_load` float NOT NULL,
41
+ `pg4_views` int(11) NOT NULL,
42
+ `pg5_avg_load` float NOT NULL,
43
+ `pg5_views` int(11) NOT NULL,
44
+ PRIMARY KEY (`id`)
45
+ ) ENGINE=MyISAM ;
46
+
47
+ ");
48
+
49
+ $installer->endSetup();
app/code/local/Aitoc/Aitloadmon/sql/aitloadmon_setup/mysql4-uninstall-1.0.0.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ if(!Mage::registry('aitloadmon_forced_disabled'))
7
+ {
8
+ $moduleKey = 'Aitoc_Aitloadmon';
9
+ $moduleKeyPaid = 'Aitoc_Aitloadmonpaid';
10
+ $filename = Mage::getBaseDir().DS.'app'.DS.'etc'.DS.'modules'.DS.$moduleKey.'.xml';
11
+ $filenamePaid = Mage::getBaseDir().DS.'app'.DS.'etc'.DS.'modules'.DS.$moduleKeyPaid.'.xml';
12
+ $enable = Mage::app()->getRequest()->getParam('enable');
13
+ if(file_exists($filenamePaid) && simplexml_load_file($filenamePaid)->modules->$moduleKeyPaid->active == 'true' && isset($enable[$moduleKeyPaid]) && $enable[$moduleKeyPaid])
14
+ {
15
+ Mage::register('aitloadmon_forced_disabled',1);
16
+ Mage::getModel('aitsys/module')->loadByModuleFile($filenamePaid)->getInstall()->uninstall();
17
+ Mage::getModel('aitsys/module')->loadByModuleFile($filename)->getInstall()->uninstall();
18
+ header('Location: '.Mage::helper('adminhtml')->getUrl('aitsys/index/index'));
19
+ die();
20
+ }
21
+ }
app/code/local/Aitoc/Aitsys/install/Aitoc_Aitloadmon.phtml ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h2 style="text-align:center; color:#FF6300;">AITOC, Inc. License Agreement<br />
2
+ for Server Load Monitor Extension for Magento.</h2>
3
+ <h3>1. General</h3>
4
+ <p>This is a legal Agreement between AITOC Inc. and Customer that covers Customer's purchase and use of Server Load Monitor Extension for Magento (the Product) developed by AITOC Inc. The extension comes in Paid and Free version.</p>
5
+ <p>By purchasing the Paid version of the Product or installing the Free version of the Product Customer is asserting with the below-stated Terms and Conditions of the present Agreement. Any violation of this Agreement will automatically deprive the Customer of the right to use the Product, thus the Customer must cease using it and terminate all copies and derivatives of the Product at Customer's disposal.</p>
6
+ <br />
7
+ <h3>2. License</h3>
8
+ <h4 style="padding-left:20px;">2.1. General Issues</h4>
9
+ <p>By purchasing the Paid version of the Product or installing the Free version of the Product Customer obtains a License which will remain valid until the Customer stops using the Product or until AITOC terminates this License because of Customer's failure to comply with any of its Terms and Conditions.</p>
10
+ <p>AITOC offers a Single Installation License for Magento extensions. Each license can be used only on one production Magento installation. It can also be used on unlimited number of test Magento installations, if the installation domains comply with the terms, described in the documentation that comes with the Product.</p>
11
+ <p>A sold License does not include ownership of any original works, part of thereof or any documentation or derivative works of the Product.</p>
12
+ <h4 style="padding-left:20px;">2.2. License Options</h4>
13
+ <p>The Paid version of the Product comes with different License Options. The Product's price depends on the number of Stores on Customer's Magento; this number is specified in the License option which is selected during the purchase.</p>
14
+ <p>The Product contains necessary controlling functionality which makes sure the Customer does not exceed the allowed number of stores. When reaching the stores limit, the Customer can extend the limitation by purchasing the License Upgrade.</p>
15
+ <p>Downgrades are not possible via standard License Upgrade functionality. For downgrades, please refer to AITOC, Inc. for a Refund. Please note that all Refunds, including downgrades, are only possible within the 30-day period from the initial purchase date.</p>
16
+ <p>Editing License file in any way other than via Administrator Control Panel User Interface will result in License revocation, Client's deprivation of right for a refund, Product update and License Upgrade, and in Product's incorrect operation. AITOC doesn't bear any responsibility for any personal or business data loss or damaging of site in case of Client's attempts to edit License functionality.</p>
17
+ <br />
18
+ <h3>3. Copyright</h3>
19
+ <p>Any unauthorized copying, renting, leasing, sublicensing, pledging, conveying, creating any Derivative Works, or disclosing of modification source codes in any way, porting of the Product to other platforms and content management systems without prior written approval from AITOC Inc. is prohibited.</p>
20
+ <p>Proofs of copyright infringement include, but are not limited to, similar code style and structures and containing similar to Product source code, layout and design. Customer is not allowed to remove the original AITOC Inc. copyright inscription from the source code of the Product. Modifications of the source code are allowed, but at own risk.</p>
21
+ <p>If revealed that the Product is used in breach of the above mentioned terms of use, AITOC, Inc. is authorized to suspend the license, advising the Customer on what actions are required to resolve the matter. In cases of a severe or repetitive violation where advice has been ignored, AITOC Inc. is authorized to revoke the License.</p>
22
+ <p>The source code and files are exclusively owned by AITOC Inc. and are subject to US Copyright Law and Copyright Laws of the State of Delaware, as well as international copyright treaty provisions. AITOC Inc. retains title to and ownership of the Product and all enhancements, modifications and updates of the Product.</p>
23
+ <p>The exclusive forum for disputes arising out of, or relating to, this Agreement shall be an appropriate federal or state court in the county of New Castle, Delaware.</p>
24
+ <br />
25
+ <h3>4. Installation</h3>
26
+ <p>Paid version of the Product package includes an automated Installer. The automated installation may depend on configuration of operational Magento solution. AITOC does not pledge for its installation module to operate successfully on modified as well as non-modified Magento solution.</p>
27
+ <p>Free version of the Product comes without the Installer.</p>
28
+ <p>The Customer may purchase installation service executed by AITOC's staff. To use the paid installation service the Customer is obliged to present correct web-site access information to AITOC support personnel, as well as valid Product License information. Installation service payments are non-refundable. AITOC will only provide paid installation services for AITOC products where the source code hasn't been modified by the Customer or any 3rd party. Failing to meet any of the above mentioned requirements deprives Customer of ability to use this service.</p>
29
+ <br />
30
+ <h3>5. Product Support Policy</h3>
31
+ <p>Free support period for the Paid version of the Product totals 6 months starting from the purchase date of the Product. The term of free support can be prolonged when a Product update is purchased. It is described in more detail in the Product Updates section.</p>
32
+ <p>License Upgrade does not prolong the terms of free support services.</p>
33
+ <p>Support team shall provide to Customer, without additional charge, all reasonably necessary written consultation requested in connection with use and operation of the Product or any problems therewith, within above-stated period of time.</p>
34
+ <p>Free support does not include achieving compatibility with third party solutions and is not provided if the Product has been customized. In cases when extraordinary support efforts will be required Customer will be offered paid support services.</p>
35
+ <p>Under extraordinary support efforts AITOC, Inc. supposes a request which probable cost to resolve may exceed the cost of the purchased Product. Support costs are calculated in accordance with average US market rates.</p>
36
+ <p>Support team preserves the right to ignore any appeals or requests, not falling under the present conditions.</p>
37
+ <p>Free version of the Product does not include free support. Any support services associated with the Free version of the Product will be paid. Support costs are calculated in accordance with average US market rates.</p>
38
+ <p>All information, acquired from AITOC, Inc. support team is advisory only and shall not create any warranty for AITOC Inc.</p>
39
+ <br />
40
+ <h3>6. Service Level Agreement</h3>
41
+ <p>AITOC, Inc. will provide support services via support tickets system on the products' pages Monday – Friday, 8 am - 5 pm GMT (3 am -12 pm EST).</p>
42
+ <br />
43
+ <h3>7. Product Updates</h3>
44
+ <p>AITOC will timely release the information about new updates in the Changelog tab on the Product pages. It is Customer's responsibility to check for updates availability. All updates can be used only as a replacement of a previously purchased Product, not as a separate copy of a Product.</p>
45
+ <p>AITOC reserves the right to determine which products will be updated and when. AITOC reserves the right to discontinue product updates.</p>
46
+ <p>Updates for the Paid version of the Product shall be available to the Customer free of charge for 6 months from the original purchase date. After that an update would cost 75% of the current product price. The discount is only applied to the same License option.</p>
47
+ <p>If needed, License Upgrade can be performed before or after the purchase of Product Update.</p>
48
+ <p>Paid update package includes: the latest version of the product, 6 extra months of free updates and support from the purchase date.</p>
49
+ <p>Customer is not obliged to purchase updates every 6 months. The decision about whether to purchase an update and when is solely Customer's right. Customer can continue using the Product after 6 months from the original purchase date and can contact AITOC for paid support services.</p>
50
+ <br />
51
+ <h3>8. Upgrade from the Free version to Paid version of the Product.</h3>
52
+ <p>There is no special mechanism for upgrading from the Free version to Paid version of the Product. The Customer who decides to switch to the Paid version of the Product may simply purchase the Paid version and install it as per the Installation Guide that comes with the product.</p>
53
+ <br />
54
+ <h3>9. Refunds</h3>
55
+ <p>Customer may demand a refund (full refund or License Downgrade) for the Paid version of the Product within a 30-day period from the date of purchase. Any clarification of Customer's refund claim within the 30-days period is optional. AITOC staff will check the compliance of all License information before
56
+ resolving money refund issue. AITOC Inc. reserves the right to deny money refund claimed within the stated period if Customer's License info is incomplete and/or incorrect.</p>
57
+ <p>Refund requests submitted after the expiration of the 30 days period shall not be accepted.</p>
58
+ <p>Refunds of money charged for custom installation service shall not be issued.</p>
59
+ <p>AITOC Inc. considers its Product refunded when the money has been withdrawn from its account. If no notification of non-acceptance has been received from client within 10 bank days, product is treated as refunded. The Customer is obliged to uninstall the product before requesting the refund and terminate all its copies and derivatives within the 5 bank days starting from the day of initiation of money refund process.</p>
60
+ <p>AITOC Inc. is not responsible for any damages neither with Magento running, nor with business profits or information occurring due to enforced manual deleting of refunded Product or its derivatives.</p>
61
+ <br />
62
+ <h3>10. Confidentiality</h3>
63
+ <p>Each party agrees that it shall not disclose any information concerning the customers, trade secrets, methods, processes or procedures or any other confidential, financial or business information of the other party, which it learns during the course of its performance of this Agreement to any third party, without the prior written consent of such other party. This obligation shall survive the cancellation or other termination of this Agreement or License. The Product contains trade secrets and proprietary know-how that belong to AITOC Inc. and it is being made available to Licensee in strict confidence.</p>
64
+ <p>AITOC, Inc. collects the information:</p>
65
+ <p>
66
+ <ul>
67
+ <li>&bull; Server name;</li>
68
+ <li>&bull; Server host name;</li>
69
+ <li>&bull; Server address (IP);</li>
70
+ <li>&bull; Magento installation version;</li>
71
+ <li>&bull; Personal identification for the Magento installation, issued by AITOC server during purchase;</li>
72
+ <li>&bull; Magento installation source code path;</li>
73
+ <li>&bull; Magento default host domain name;</li>
74
+ <li>&bull; Magento current host domain name;</li>
75
+ <li>&bull; Magento admin area path;</li>
76
+ <li>&bull; Module's key name;</li>
77
+ <li>&bull; Module's version;</li>
78
+ <li>&bull; Module's identification number in AITOC DB;</li>
79
+ <li>&bull; License purchase identification number.</li>
80
+ </ul>
81
+ </p>
82
+ <p>This information is collected once, upon installation, and is mandatory to ensure the Product's valid purchase and exploitation.</p>
83
+ <br />
84
+ <p>Paid and Free versions of Server Load Monitor extension also collect the following information periodically in order to configure and recommend you better server settings:</p>
85
+ <p>
86
+ <ul>
87
+ <li>&bull; Number of products in your Magento installation;</li>
88
+ <li>&bull; Number of orders in your Magento installation;</li>
89
+ <li>&bull; Number of customers;</li>
90
+ <li>&bull; Number of quotes;</li>
91
+ <li>&bull; Magento version;</li>
92
+ <li>&bull; Server information:
93
+ <ul>
94
+ <li style="padding-left:20px;">&bull; General information about server's CPU;</li>
95
+ <li style="padding-left:20px;">&bull; Number of cores on your server's CPU;</li>
96
+ <li style="padding-left:20px;">&bull; CPU frequency;</li>
97
+ <li style="padding-left:20px;">&bull; Measurement of CPU speed made by the Linux kernel when it boots to calibrate an internal busy-loop;</li>
98
+ <li style="padding-left:20px;">&bull; CPU name;</li>
99
+ <li style="padding-left:20px;">&bull; Available RAM on your server;</li>
100
+ <li style="padding-left:20px;">&bull; Type of your server (virtual or dedicated);</li>
101
+ <li style="padding-left:20px;">&bull; Whether AITOC's Magento Booster extension is installed;</li>
102
+ </ul>
103
+ <li>&bull; Statistic of number of visitors and page load time.</li>
104
+ </ul>
105
+ </p>
106
+ <p>This information will be impersonalized and will not be associated to any personal data that may allow AITOC or any 3d party to identify any particular Magento installation or host. This information will be used to analyze Magento performance under different conditions and suggest appropriate hardware that may improve Magento's performance. Paid version of the Product allows to disable sending this data to AITOC.</p>
107
+ <br />
108
+ <h3>11. Disclaimer of Responsibility</h3>
109
+ <p>AITOC Inc. Products are not stand-alone solutions and require Magento to be installed primarily with a valid Magento License. AITOC Inc. does not guarantee for its Product to operate correctly on any Magento solution, either modified or non-modified. AITOC Inc. does not bear any responsibility for damages or incorrect running of the Magento solution, occurred due to installation or utilizing of Module. AITOC Inc. disclaims any accountability for content of a site on which the Product is installed. Complaints received from any third party may result in License suspension or revocation if the matter cannot be resolved. License suspension action is at the sole discretion of AITOC Inc. and is intended to offer both parties time for settlement or advice. License reinstatement shall remain Vendor's exclusive privilege. AITOC Inc. is not liable to Customer or any third party for any damages including, but not limited to, loss of business or business profits, loss of business information due to using AITOC Inc. Products. By accepting this License Agreement Customer acknowledges to use the Product at own risk and has clear understanding of responsibility for any damage to own computer system and data.</p>
110
+ <br />
111
+ <h3>12. Changes in Conditions</h3>
112
+ <p>AITOC Inc. reserves the right to modify these agreement terms at any time. It is solely Customer's responsibility to inquire about any changes made to the present Agreement.</p>
app/design/adminhtml/default/default/layout/aitloadmon.xml ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <adminhtml_aitloadmon_index>
4
+ <reference name="head">
5
+ <action method="addJs"><script>aitoc/aitloadmon/raphael-min.js</script></action>
6
+ <action method="addJs"><script>aitoc/aitloadmon/grafico.min.js</script></action>
7
+ <action method="addJs"><script>aitoc/aitloadmon/grafico.line.custom.js</script></action>
8
+ <action method="addJs"><script>aitoc/aitloadmon/grafico.bar.custom.js</script></action>
9
+ </reference>
10
+ <reference name="content">
11
+ <block type="aitloadmon/adminhtml_aitloadmon" name="main_bl" template="aitloadmon/graph.phtml">
12
+ <block type="aitloadmon/adminhtml_aitloadmon_filter" name="aitload_filter" as="filter" />
13
+ <block type="aitloadmon/adminhtml_aitloadmon_lineGraph" name="aitload_lineGraph" as="lineGraph" template="aitloadmon/line.phtml" />
14
+ <block type="aitloadmon/adminhtml_aitloadmon_barGraph" name="aitload_barGraph" as="barGraph" template="aitloadmon/bar.phtml" />
15
+ <block type="core/template" name="aitload_quickFilter" as="quickFilter" template="aitloadmon/quick.phtml" />
16
+ </block>
17
+ <block type="core/template" name="system" as="system_block" template="aitloadmon/system.phtml"/>
18
+ </reference>
19
+ </adminhtml_aitloadmon_index>
20
+ <adminhtml_aitloadmon_manage>
21
+ <reference name="content">
22
+ <block type="core/template" name="back" as="back_button" template="aitloadmon/back.phtml"/>
23
+ <block type="aitloadmon/adminhtml_manage" name="manage" as="manage_data"/>
24
+ <block type="core/template" name="system" as="system_block" template="aitloadmon/system.phtml"/>
25
+ </reference>
26
+ </adminhtml_aitloadmon_manage>
27
+ <adminhtml_calculator_index>
28
+ <reference name="content">
29
+ <block type="core/template" name="sysInfo" as="sysInfo_block" template="aitloadmon/sysInfo.phtml"/>
30
+ <!--block type="aitloadmon/adminhtml_calculator" name="aitload_calculator" as="calculator" /-->
31
+ </reference>
32
+ </adminhtml_calculator_index>
33
+ <adminhtml_calculator_submit>
34
+ <reference name="content">
35
+ <block type="aitloadmon/adminhtml_calculatorResult" name="aitload_calculator_result" as="calculator_result" />
36
+ </reference>
37
+ </adminhtml_calculator_submit>
38
+ <default>
39
+ <reference name="content">
40
+ <block type="aitloadmon/adminhtml_indicator" name="aitloadmon_indicator" template="aitloadmon/indicator.phtml"/>
41
+ </reference>
42
+ </default>
43
+
44
+ </layout>
45
+ <!--
46
+ /**
47
+ * @copyright Copyright (c) 2013 AITOC, Inc.
48
+ * @license License agreement could be found at the root folder of this package
49
+ */
50
+ -->
app/design/adminhtml/default/default/template/aitloadmon/back.phtml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ ?>
7
+ <input type="button" class="form-button submit" title="Return to System Configuration" value="Return to System Configuration" id="return" onclick="document.location.href='<?php echo Mage::helper('adminhtml')->getUrl('adminhtml/system_config/edit',array('section'=>'system')); ?>'" style="float: right; margin-bottom: 10px;">
8
+ <div style="clear: both;"></div>
app/design/adminhtml/default/default/template/aitloadmon/bar.phtml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ ?>
7
+ <div class="entry-edit">
8
+ <div class="entry-edit-head">
9
+ <h4 class="icon-head head-edit-form fieldset-legend"><?php echo Mage::helper('aitloadmon')->__('Avg Load / Max Load for the selected period by groups');?></h4>
10
+ </div>
11
+ <div class="fieldset">
12
+ <div id="graph2" style="height: 200px; width: 1000px;"></div>
13
+ </div>
14
+ </div>
15
+ <script type="text/javascript">
16
+ var graph2 = new Grafico.HorizontalBarGraphCustom($('graph2'), {
17
+ <?php echo $this->getBarGraphData(); ?>
18
+ },
19
+ {
20
+ labels: <?php echo $this->getBarLabels($this->getCompare()); ?>,
21
+ show_ticks: false,
22
+ compare_mode: <?php echo (int)$this->getCompare();?>,
23
+ vertical_label_unit : "s",
24
+ colors: {
25
+ max_load: '#A0151E',
26
+ avg_load: ['#877D1E',"#6016CA","#1680CA","#F5861A","#1B7B00"]
27
+ }
28
+ }
29
+ );
30
+ </script>
app/design/adminhtml/default/default/template/aitloadmon/graph.phtml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ ?>
7
+ <ul class="messages">
8
+ <?php if(!Mage::getStoreConfig('system/aitloadmon/disable_calc_info_sending')):?>
9
+ <li class="notice-msg">
10
+ <p><?php echo Mage::helper('aitloadmon')->__('Please note that collected data of Server Load Monitor extension will be sent to AITOC’s Magento Server Configuration Calculator in order to configure and recommend you better server settings.');?></p>
11
+ </li>
12
+ <?php endif; ?>
13
+ <li class="notice-msg">
14
+ <p><?php echo Mage::helper('aitloadmon')->__('Please note that the server time is used instead of your local one.');?></p>
15
+ </li>
16
+ </ul>
17
+ <?php echo $this->getChildHtml('quickFilter'); ?>
18
+ <?php echo $this->getChildHtml('filter'); ?>
19
+ <?php if(!$this->isEnoughDataToBuildGraph($this->getCollection()) || ($this->getCompare() && !$this->isEnoughDataToBuildGraph($this->getCompareCollection()))):?>
20
+ <ul class="messages">
21
+ <li class="error-msg">
22
+ <p><?php echo Mage::helper('aitloadmon')->__('Sorry, there is not enough data for the selected period(s).');?></p>
23
+ </li>
24
+ </ul>
25
+ <?php else: ?>
26
+ <?php echo $this->getChildHtml('barGraph'); ?>
27
+ <?php echo $this->getChildHtml('lineGraph'); ?>
28
+ <?php endif;?>
app/design/adminhtml/default/default/template/aitloadmon/indicator.phtml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ ?>
7
+ <div style="position: fixed; top: 35px; right: 3px; background-color: #cccccc; height: 18px; margin: 0; padding: 0;" onmouseover="$('aitloadmon_notice').show();" onmouseout="$('aitloadmon_notice').hide();">
8
+ <div id="aitloadmon_notice" style="display:none; float: left;">&nbsp;<?php echo Mage::helper('aitloadmon')->__('Current Server Load:');?>&nbsp;</div>
9
+ <a href="<?php echo Mage::helper("adminhtml")->getUrl('adminhtml/aitloadmon/index');?>">
10
+ <div style="background-color: <?php echo $this->getColor();?>; height: 16px; width: 16px; float: left; border: 1px solid #cccccc;"></div>
11
+ </a>
12
+ </div>
app/design/adminhtml/default/default/template/aitloadmon/line.phtml ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ ?>
7
+ <style>
8
+ .linegraph_check
9
+ {
10
+ margin-right: 5px;
11
+ margin-left: 25px;
12
+ }
13
+ </style>
14
+ <div class="entry-edit">
15
+ <div class="entry-edit-head">
16
+ <h4 class="icon-head head-edit-form fieldset-legend"><?php echo Mage::helper('aitloadmon')->__(($this->getType()=='max')?'MAXIMUM':'AVERAGE').Mage::helper('aitloadmon')->__(' LOAD for the selected period by groups and time');?></h4>
17
+ </div>
18
+ <div class="fieldset">
19
+ <div><?php echo Mage::helper('aitloadmon')->__('Show graphs for:'); ?><?php echo $this->getCheckBoxes(); ?></div>
20
+ <div id="graph1" style="height: 340px; width: 1000px;"></div>
21
+ <?php if($this->getCompare()):?><div id="graph1compare" style="height: 340px; width: 1000px;"></div><?php endif;?>
22
+ </div>
23
+ </div>
24
+ <script type="text/javascript">
25
+ var lineGraphs = [];
26
+ function toggleLines(obj)
27
+ {
28
+ var graphId = $(obj).getAttribute('rel');
29
+ if(lineGraphs[graphId])
30
+ {
31
+ if(!$(obj).checked)
32
+ {
33
+ lineGraphs[graphId].each(function(element){element.hide()});
34
+ }
35
+ else
36
+ {
37
+ lineGraphs[graphId].each(function(element){element.show()});
38
+ }
39
+ }
40
+ }
41
+ <?php $lineGraph = $this->getLineGraphData(); ?>
42
+ var graph1 = new Grafico.LineGraphCustom($('graph1'),
43
+ {
44
+ <?php echo $lineGraph['data']; ?>
45
+ },
46
+ {
47
+ markers : "value",
48
+ grid : true,
49
+ draw_axis : true,
50
+ plot_padding : 0,
51
+ stroke_width : 2,
52
+ show_vertical_labels : true,
53
+ show_horizontal_labels : true,
54
+ vertical_label_unit: 's',
55
+ label_rotation : 0,
56
+ labels : <?php echo $lineGraph['labels']; ?>,
57
+ dot_labels : <?php echo $lineGraph['dot_labels']; ?>,
58
+ label_sizes : {<?php echo $lineGraph['dataSize']; ?>},
59
+ hover_color : "#000",
60
+ hover_text_color : "#fff",
61
+ opacity: 1,
62
+ colors: {
63
+ other: "#877D1E",
64
+ cms: "#6016CA",
65
+ checkout: "#1680CA",
66
+ catalogsearch: "#F5861A",
67
+ catalog: "#1B7B00"
68
+ },
69
+ datalabels : {
70
+ other: "Other pages",
71
+ cms: "CMS pages",
72
+ checkout: "Checkout pages",
73
+ catalogsearch: "Search pages",
74
+ catalog: "Catalog pages"
75
+ }
76
+ });
77
+
78
+ <?php if($this->getCompare()):?>
79
+ <?php $lineGraph = $this->getLineGraphDataCompared(); ?>
80
+ var graph1 = new Grafico.LineGraphCustom($('graph1compare'),
81
+ {
82
+ <?php echo $lineGraph['data']; ?>
83
+ },
84
+ {
85
+ markers : "value",
86
+ grid : true,
87
+ draw_axis : true,
88
+ plot_padding : 0,
89
+ stroke_width : 2,
90
+ show_vertical_labels : true,
91
+ show_horizontal_labels : true,
92
+ label_rotation : 0,
93
+ labels : <?php echo $lineGraph['labels']; ?>,
94
+ dot_labels : <?php echo $lineGraph['dot_labels']; ?>,
95
+ label_sizes : {<?php echo $lineGraph['dataSize']; ?>},
96
+ hover_color : "#000",
97
+ hover_text_color : "#fff",
98
+ opacity: 0.5,
99
+ colors: {
100
+ other: "#877D1E",
101
+ cms: "#6016CA",
102
+ checkout: "#1680CA",
103
+ catalogsearch: "#F5861A",
104
+ catalog: "#1B7B00"
105
+ },
106
+ datalabels : {
107
+ other: "Other pages",
108
+ cms: "CMS pages",
109
+ checkout: "Checkout pages",
110
+ catalogsearch: "Search pages",
111
+ catalog: "Catalog pages"
112
+ }
113
+ });
114
+ <?php endif;?>
115
+ </script>
app/design/adminhtml/default/default/template/aitloadmon/quick.phtml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ ?>
7
+ <div class="entry-edit">
8
+ <div class="entry-edit-head">
9
+ <h4 class="icon-head head-edit-form fieldset-legend"><?php echo Mage::helper('aitloadmon')->__('Basic Filter Options');?></h4>
10
+ </div>
11
+ <div class="fieldset">
12
+ <input type="button" class="form-button" title="Last Hour" value="Last Hour" id="qickfilter0" onclick="quickFilter('<?php echo date('Y-m-d H:i:s', time()-3600+1);?>','<?php echo date('Y-m-d H:i:s');?>');">
13
+ <input type="button" class="form-button" title="Last 24h" value="Last 24h" id="qickfilter1" onclick="quickFilter('<?php echo date('Y-m-d H:i:s', time()-86400+1);?>','<?php echo date('Y-m-d H:i:s');?>');">
14
+ <input type="button" class="form-button" title="Last Week" value="Last Week" id="qickfilter2" onclick="quickFilter('<?php echo date('Y-m-d H:i:s', time()-86400*7+1);?>','<?php echo date('Y-m-d H:i:s');?>');">
15
+ <input type="button" class="form-button" title="Last Month" value="Last Month" id="qickfilter3" onclick="quickFilter('<?php echo date('Y-m-d H:i:s', time()-86400*30+1);?>','<?php echo date('Y-m-d H:i:s');?>');">
16
+ <input type="button" class="form-button" title="Last Year" value="Last Year" id="qickfilter4" onclick="quickFilter('<?php echo date('Y-m-d H:i:s', time()-86400*365+1);?>','<?php echo date('Y-m-d H:i:s');?>');">
17
+ </div>
18
+ </div>
19
+ <script type="text/javascript">
20
+ function quickFilter($start,$end)
21
+ {
22
+ $('starts').value = $start;
23
+ $('ends').value = $end;
24
+ $('compare').checked = false;
25
+ $('edit_form').submit();
26
+
27
+ }
28
+ </script>
app/design/adminhtml/default/default/template/aitloadmon/sysInfo.phtml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ ?>
7
+ <?php if($system = Mage::getModel('aitloadmon/sysInfo')->getSystem()): ?>
8
+ <div><?php echo $system;?></div>
9
+ <?php else:?>
10
+ <iframe src="<?php echo Mage::getModel('aitloadmon/sysInfo')->getSystemUrl();?>" style="width: 100%; border: medium none;"></iframe>
11
+ <?php endif;?>
app/design/adminhtml/default/default/template/aitloadmon/system.phtml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ ?>
7
+ <?php if($system = Mage::getModel('aitloadmon/system')->getSystem()): ?>
8
+ <div><?php echo $system;?></div>
9
+ <?php else:?>
10
+ <iframe src="<?php echo Mage::getModel('aitloadmon/system')->getSystemUrl();?>" style="width: 100%; border: medium none;"></iframe>
11
+ <?php endif;?>
app/etc/modules/Aitoc_Aitloadmon.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Aitoc_Aitloadmon>
5
+ <active>true</active>
6
+ <codePool>local</codePool>
7
+ <self_name>Server Load Monitor Free</self_name>
8
+ </Aitoc_Aitloadmon>
9
+ </modules>
10
+ </config>
11
+ <!--
12
+ /**
13
+ * @copyright Copyright (c) 2013 AITOC, Inc.
14
+ * @license License agreement could be found at the root folder of this package
15
+ */
16
+ -->
js/aitoc/aitloadmon/grafico.bar.custom.js ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @copyright Copyright (c) 2013 AITOC, Inc.
3
+ * @license License agreement could be found at the root folder of this package
4
+ */
5
+ Grafico.HorizontalBarGraphCustom = Class.create(Grafico.BarGraph, {
6
+ chartDefaults: function () {
7
+ return {
8
+ bar : true,
9
+ horizontalbar : true,
10
+ plot_padding : 0,
11
+ horizontal_rounded : false,
12
+ bargraph_lastcolor : false
13
+ };
14
+ },
15
+
16
+ setChartSpecificOptions: function () {
17
+ // Approximate the width required by the labels
18
+ this.x_padding_left = 20 + this.longestLabel() * (this.options.font_size / 2);
19
+ this.bar_padding = 5;
20
+ this.bar_width = this.calculateBarHeight();
21
+ this.step = this.calculateStep();
22
+ this.graph_width = this.options.width - this.x_padding_right - this.x_padding_left;
23
+ },
24
+
25
+ normalise: function (value) {
26
+ var range = this.makeValueLabels(this.y_label_count);
27
+ range = range[range.length-1];
28
+ return ((value / range) * this.graph_width);
29
+ },
30
+
31
+ longestLabel: function () {
32
+ return $A(this.options.labels).sort(function (a, b) { return a.toString().length < b.toString().length; }).first().toString().length;
33
+ },
34
+
35
+ /* Height */
36
+ calculateBarHeight: function () {
37
+ return (this.graph_height / this.data_size) - this.bar_padding;
38
+ },
39
+
40
+ calculateStep: function () {
41
+ return (this.graph_height - (this.options.plot_padding * 2)) / this.data_size;
42
+ },
43
+
44
+ drawLines: function (label, color, data, datalabel, element, graphindex) {
45
+ var y = this.y_padding_top + (this.bar_padding / 2) -0.5,
46
+ offset = this.zero_value * (this.graph_width / this.y_label_count),
47
+ x = this.x_padding_left + offset - 0.5,
48
+ lastcolor = this.options.bargraph_lastcolor,
49
+ negativecolor = this.options.bargraph_negativecolor || color;
50
+ this.datalabel = datalabel;
51
+ $A(data).each(function (value, index) {
52
+ var color2,
53
+ horizontal_rounded = this.options.horizontal_rounded ? this.bar_width / 2 : 0,
54
+ bargraph;
55
+
56
+ if(this.options.compare_mode)
57
+ {
58
+ var opacity = (index%2)?0.5:1;
59
+ }
60
+ else
61
+ {
62
+ var opacity = 1;
63
+ }
64
+
65
+ if(color instanceof Array)
66
+ {
67
+ if(this.options.compare_mode)
68
+ {
69
+ tmp_index = Math.floor(index / 2);
70
+ }
71
+ else
72
+ {
73
+ tmp_index = index;
74
+ }
75
+ tmp_color = color[tmp_index];
76
+ }
77
+ else
78
+ {
79
+ tmp_color = color;
80
+ }
81
+
82
+ if (lastcolor && index === data.length-1) {
83
+ color2 = lastcolor;
84
+ } else {
85
+ color2 = value < 0 ? negativecolor : tmp_color;
86
+ }
87
+
88
+ value = value / this.graph_width * (this.graph_width - offset);
89
+ bargraph = this.paper.rect(x, y, value, this.bar_width, horizontal_rounded).attr({fill: color2, 'stroke-width': 0, stroke : color2, 'stroke-opacity' : 0, 'fill-opacity' : opacity});
90
+
91
+ if (value < 0) {
92
+ bargraph.attr({width: -bargraph.attrs.width}).translate(value, 0);
93
+ }
94
+
95
+ if (horizontal_rounded) {
96
+ var bargraphset = this.paper.set(),
97
+ bargraph2 = this.paper.rect(x, y, value - this.bar_width/2, this.bar_width).attr({fill: color2, 'stroke-width': 0, stroke : color2, 'stroke-opacity' : 0});
98
+
99
+ bargraphset.push(bargraph2, bargraph);
100
+
101
+ if (value < 0) {
102
+ bargraph2.attr({width: -bargraph2.attrs.width - this.bar_width}).translate(value + this.bar_width/2, 0);
103
+ }
104
+ }
105
+
106
+ if (this.options.datalabels) {
107
+ var hover_color = this.options.hover_color || color2,
108
+ hoverSet = this.paper.set(),
109
+ datalabel = this.datalabel[index].toString(),
110
+ text = this.paper.text(offset + value + this.x_padding_left / 2, bargraph.attrs.y - (this.options.font_size * 1.5), datalabel)
111
+ .attr({'font-size': this.options.font_size, fill: this.options.hover_text_color, opacity: 1}),
112
+ hoverbar = this.paper.rect(
113
+ this.x_padding_left,
114
+ y,
115
+ this.graph_width,
116
+ this.bar_width).attr({fill: color2, 'stroke-width': 0, stroke: color2, opacity: 0}
117
+ ),
118
+ textbox = text.getBBox();
119
+
120
+ if (value < 0) { text.translate(textbox.width, 0); }
121
+
122
+ var textpadding = 4,
123
+ roundRect = this.drawRoundRect(text, textbox, textpadding),
124
+ nib = this.drawNib(text, textbox, textpadding);
125
+
126
+ hoverSet.push(roundRect,nib,text).attr({opacity: 0});
127
+ this.checkHoverPos({rect: roundRect, set: hoverSet, nib: nib});
128
+ this.globalHoverSet.push(hoverSet);
129
+ this.globalBlockSet.push(hoverbar);
130
+ if (roundRect.attrs.y < 0) {
131
+ hoverSet.translate(0, 1 - roundRect.attrs.y);
132
+ }
133
+
134
+ hoverbar.hover(function (event) {
135
+ if (horizontal_rounded) {
136
+ bargraphset.animate({fill: hover_color, stroke: hover_color}, 200);
137
+ } else {
138
+ bargraph.animate({fill: hover_color, stroke: hover_color}, 200);
139
+ }
140
+ hoverSet.animate({opacity: 1}, 200);
141
+ }, function (event) {
142
+ if (horizontal_rounded) {
143
+ bargraphset.animate({fill: color2, stroke: color2}, 200);
144
+ } else {
145
+ bargraph.animate({fill: color2, stroke: color2}, 200);
146
+ }
147
+ hoverSet.animate({opacity: 0}, 200);
148
+ });
149
+ }
150
+ y = y + this.step;
151
+ }.bind(this));
152
+ },
153
+
154
+ /* Horizontal version */
155
+ drawFocusHint: function () {
156
+ var length = 5,
157
+ x = this.x_padding_left + length * 2,
158
+ y = this.options.height - this.y_padding_bottom - length / 2;
159
+
160
+ this.paper.path()
161
+ .attr({stroke: this.options.label_color, 'stroke-width': 2})
162
+ .moveTo(x, y)
163
+ .lineTo(x - length, y + length)
164
+ .moveTo(x - length, y)
165
+ .lineTo(x - (length * 2), y + length);
166
+ },
167
+
168
+ drawVerticalLabels: function () {
169
+ var y_start = (this.step / 2),
170
+ extra_options = this.options.label_rotation ? {"text-anchor": 'end', rotation:this.options.label_rotation, translation: "0 " + this.options.font_size/2} : {"text-anchor": 'end'},
171
+ labels = this.options.labels;
172
+
173
+ if (this.options.label_max_size) {
174
+ for (var i = 0; i < labels.length; i++) {
175
+ labels[i] = labels[i].truncate(this.options.label_max_size + 1, "тАж");
176
+ }
177
+ }
178
+
179
+ this.drawMarkers(this.options.labels.reverse(), [0, -1], this.step, y_start, [-8, -(this.options.font_size / 5)], extra_options);
180
+ },
181
+
182
+ drawHorizontalLabels: function () {
183
+ var x_step = this.graph_width / this.y_label_count,
184
+ x_labels = this.makeValueLabels(this.y_label_count);
185
+
186
+ if (this.options.vertical_label_unit) {
187
+ for (var i = 0; i < x_labels.length; i++) {
188
+ x_labels[i] += this.options.vertical_label_unit;
189
+ }
190
+ }
191
+ this.drawMarkers(x_labels, [1, 0], x_step, x_step, [0, (this.options.font_size + 7) * -1]);
192
+ },
193
+
194
+ drawMeanLine: function (data) {
195
+ var offset = $A(data).inject(0, function (value, sum) { return sum + value; }) / data.length;
196
+ offset = this.options.bar ? offset + (this.zero_value * (this.graph_height / this.y_label_count)) : offset;
197
+
198
+ this.paper.path()
199
+ .attr(this.options.meanline)
200
+ .moveTo(this.x_padding_left - 1 + offset, this.y_padding_top).
201
+ lineTo(this.x_padding_left - 1 + offset, this.y_padding_top + this.graph_height);
202
+ }
203
+ });
204
+
js/aitoc/aitloadmon/grafico.line.custom.js ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @copyright Copyright (c) 2013 AITOC, Inc.
3
+ * @license License agreement could be found at the root folder of this package
4
+ */
5
+ Grafico.LineGraphCustom = Class.create(Grafico.BaseGraph, {
6
+ chartDefaults: function () {
7
+ return {
8
+ line: true,
9
+ start_at_zero: true,
10
+ stroke_width: 5,
11
+ curve_amount: 10
12
+ };
13
+ },
14
+
15
+ setChartSpecificOptions: function () {
16
+ },
17
+
18
+ calculateStep: function () {
19
+ return (this.graph_width - (this.options.plot_padding * 2)) / (this.data_size - 1);
20
+ },
21
+
22
+ startPlot: function (cursor, x, y, color) {
23
+ cursor.moveTo(x, y);
24
+ cursor.attr({'stroke-opacity': this.options.opacity});
25
+
26
+ },
27
+
28
+ drawPlot: function (index, cursor, x, y, color, coords, datalabel, element, graphindex) {
29
+
30
+ if(typeof lineGraphs[graphindex+1] == 'undefined')lineGraphs[graphindex+1]=[];
31
+
32
+ if (this.options.markers === 'circle') {
33
+ this.drawGraphMarkers(index, x, y, color, datalabel, element);
34
+ } else if (this.options.markers === 'value') {
35
+ this.drawGraphValueMarkers(index, x, y, color, datalabel, element, graphindex);
36
+ }
37
+
38
+ if (index === 0) {
39
+ lineGraphs[graphindex+1].push(cursor);
40
+ return this.startPlot(cursor, x - 0.5, y, color);
41
+ }
42
+ if (this.options.curve_amount) {
43
+ ww = cursor.cplineTo(x, y, this.options.curve_amount);
44
+ } else {
45
+ ww = cursor.lineTo(x, y);
46
+
47
+ }
48
+ },
49
+
50
+ drawGraphMarkers: function (index, x, y, color, datalabel, element) {
51
+ var circle = this.paper.circle(x, y, this.options.marker_size),
52
+ old_marker_size = this.options.marker_size,
53
+ color2 = this.options.hover_color || color,
54
+ new_marker_size = parseInt(1.7 * old_marker_size, 10);
55
+
56
+ circle.attr({ 'stroke-width': '1px', stroke: this.options.background_color, fill: color });
57
+ this.globalMarkerSet.push(circle);
58
+
59
+ circle.hover(function (event) {
60
+ circle.animate({r: new_marker_size, fill: color2}, 200);
61
+ }, function (event) {
62
+ circle.animate({r: old_marker_size, fill: color}, 200);
63
+ });
64
+ },
65
+
66
+ drawGraphValueMarkers: function (index, x, y, color, datalabel, element, graphindex) {
67
+ index += this.options.odd_horizontal_offset>1 ? this.options.odd_horizontal_offset : 0;
68
+ index -= this.options.stacked_fill || this.options.area ? 1 : 0;
69
+
70
+ if(!this.options.label_sizes[graphindex+1][index])
71
+ {
72
+ tmpsize = 1;
73
+ }
74
+ else
75
+ {
76
+ tmpsize = this.options.label_sizes[graphindex+1][index];
77
+ }
78
+
79
+ var circle = this.paper.circle(x, y, Math.log(tmpsize));
80
+
81
+ circle.attr({ 'stroke-width': '1px', stroke: this.options.background_color, fill: color, 'fill-opacity': this.options.opacity});
82
+ this.globalMarkerSet.push(circle);
83
+
84
+ lineGraphs[graphindex+1].push(circle);
85
+
86
+ var currentset = this.options.stacked ? this.real_data : this.data_sets,
87
+ currentvalue = currentset.collect(function (data_set) { return data_set[1][index]; })[graphindex];
88
+
89
+ if (currentvalue) {
90
+ currentvalue = "" + currentvalue.toString().split('.');
91
+ if (currentvalue[1]) {
92
+ currentvalue[1] = currentvalue[1].truncate(3, '');
93
+ }
94
+ }
95
+ if (
96
+ (this.options.line||this.options.stacked) || //if the option is a line graph
97
+ ((this.options.stacked_fill||this.options.area) && index != -1) && //if it's stacked or an area and it's not the first
98
+ typeof currentvalue != "undefined") { //if there is a current value
99
+
100
+ var rectx = x-(this.step/2),
101
+ recty = y-[this.options.stroke_width/2, this.options.hover_radius].max(),
102
+ rectw = this.step,
103
+ recth = [this.options.stroke_width, this.options.hover_radius*2].max(),
104
+ circle = this.paper.circle(x, y, this.options.marker_size == 0 ? [this.options.stroke_width*1.5, this.step].min() : this.options.marker_size).attr({ 'stroke-width': '1px', stroke: this.options.background_color, fill: color,opacity:0}),
105
+ block = this.paper.rect(rectx, recty, rectw, recth).attr({fill:color, 'stroke-width': 0, stroke : color,opacity:0});
106
+
107
+ if (this.options.datalabels) {
108
+ if(typeof(datalabel) == 'function') {
109
+ datalabel = datalabel.call(this, index, currentvalue);
110
+ } else {
111
+ datalabel = datalabel + ": " + currentvalue + " s\r\n" + "Page views: " + this.options.label_sizes[graphindex+1][index] + "\r\n" + this.options.dot_labels[index];
112
+ }
113
+ } else {
114
+ datalabel = "" + currentvalue;
115
+ }
116
+ datalabel += this.options.vertical_label_unit ? " " + this.options.vertical_label_unit : "";
117
+
118
+ var hoverSet = this.paper.set(),
119
+ textpadding = 4,
120
+ text = this.paper.text(circle.attrs.cx, circle.attrs.cy - (this.options.font_size * 1.5) -2 * textpadding, datalabel).attr({'font-size': this.options.font_size, fill:this.options.hover_text_color, opacity: 1}),
121
+ textbox = text.getBBox(),
122
+ roundRect= this.drawRoundRect(text, textbox, textpadding),
123
+ nib = this.drawNib(text, textbox, textpadding);
124
+
125
+ hoverSet.push(circle,roundRect,nib,text).attr({opacity:0});
126
+ lineGraphs[graphindex+1].push(circle,roundRect,nib,text);
127
+ this.checkHoverPos({rect:roundRect,set:hoverSet,marker:circle,nib:nib,textpadding:textpadding});
128
+ this.globalHoverSet.push(hoverSet);
129
+ this.globalBlockSet.push(block);
130
+
131
+ block.hover(function (event) {
132
+ hoverSet.animate({opacity:1},200);
133
+ }, function (event) {
134
+ hoverSet.animate({opacity:0},200);
135
+ });
136
+ }
137
+ }
138
+ });
js/aitoc/aitloadmon/grafico.min.js ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var Grafico={Version:"0.9.1",Base:{},BaseGraph:{},Normaliser:{},LineGraph:{},AreaGraph:{},StackGraph:{},StreamGraph:{},BarGraph:{},StackedBarGraph:{},HorizontalBarGraph:{},SparkLine:{},SparkBar:{}};
2
+ Grafico.Base=Class.create({normaliseData:function(a){return $A(a).collect(function(a){return this.normalise(a)}.bind(this))},deepCopy:function(a){var b,c,d;if(Object.prototype.toString.call(a)==="[object Array]"){b=[];d=a.length;for(c=0;c<d;c++)b[c]=arguments.callee(a[c]);return b}if(typeof a==="object"){b={};for(c in a)b[c]=arguments.callee(a[c]);return b}return a}});
3
+ Grafico.Normaliser=Class.create({initialize:function(a,b){this.options={start_value:void 0,number_of_tickmarks:10};Object.extend(this.options,b||{});this.min=a.min();this.max=a.max();this.step=this.range=this.zero_value=this.start_value=void 0;if(this.options.start_value!==void 0)this.zero_value=this.start_value=this.options.start_value;this.min==0&&this.min==this.max?(this.zero_value=this.start_value=0,this.step=this.range=1):this.looseLabels(this.start_value!==void 0&&this.min>=0?this.start_value:
4
+ this.min,this.max)},looseLabels:function(a,b){var c=this.niceNumber((a==b?this.niceNumber(Math.abs(b),!1):this.niceNumber(b-a,!1))/(this.options.number_of_tickmarks-1),!0),d=[-Math.floor(Math.LOG10E*Math.log(c)),0].max(),e=this.floorToPrecision(a/c,d)*c,f=this.ceilToPrecision(b/c,d)*c,g=this.roundToPrecision(this.niceNumber(0.5*c,!0),d);this.step=this.roundToPrecision(c,d);this.max<=0?f=[f+g,0].min():f+=g;this.min<0?e-=g:e=[e-g,0].max();a!==b&&(e=this.roundToOrigin(e,1));this.range=this.roundToPrecision(Math.abs(this.ceilToPrecision(f,
5
+ d)-this.floorToPrecision(e,d)),d);this.start_value=this.floorToPrecision(e,d);this.zero_value=this.roundToPrecision(Math.abs(e)/this.step,d)},roundToPrecision:function(a,b){var c=Math.pow(10,b);return Math.round(a*c)/c},floorToPrecision:function(a,b){var c=Math.pow(10,b);return Math.floor(a*c)/c},ceilToPrecision:function(a,b){var c=Math.pow(10,b);return Math.ceil(a*c)/c},niceNumber:function(a,b){var c=Math.floor(Math.LOG10E*Math.log(a)),d;c=c<0?parseFloat(Math.pow(10,c).toFixed(Math.abs(c))):Math.pow(10,
6
+ c);d=a/c;return(b?d<1.5?1:d<3?2:d<7?5:10:d<=1?1:d<=2?2:d<=5?5:10)*c},roundToOrigin:function(a,b){var c=a;c=Math.pow(10,-(b||1));c=Math.round(a*c)/c;return c>this.min?this.roundToOrigin(a-this.step):c}});
7
+ Grafico.BaseGraph=Class.create(Grafico.Base,{initialize:function(a,b,c){this.options={width:parseInt(a.getStyle("width"),10),height:parseInt(a.getStyle("height"),10),grid:!0,show_vertical_grid:!0,show_horizontal_grid:!0,plot_padding:10,font_size:10,show_horizontal_labels:!0,show_vertical_labels:!0,show_ticks:!0,vertical_label_unit:"",background_color:"#fff",label_color:"#000",grid_color:"#ccc",hover_text_color:"#fff",markers:!1,marker_size:5,meanline:!1,padding_top:20,draw_axis:!0,datalabels:"",hover_color:"",
8
+ hover_radius:15,watermark:!1,watermark_location:!1,hide_empty_label_grid:!1,left_padding:!1,right_padding:!1,label_rotation:0,label_max_size:!1,focus_hint:!0,min:0,max:null,normaliser:Grafico.Normaliser};Object.extend(this.options,this.chartDefaults()||{});Object.extend(this.options,c||{});this.element=a;this.data_sets=Object.isArray(b)?new Hash({one:b}):$H(b);if(this.chartDefaults().stacked===!0)this.real_data=this.deepCopy(this.data_sets),this.data_sets=this.stackData(this.data_sets);this.flat_data=
9
+ this.data_sets.collect(function(a){return a[1]}).flatten();if(this.hasBaseLine())this.flat_data.push(this.base_line),this.flat_data=this.flat_data.flatten();this.normaliser=new this.options.normaliser(this.flat_data,this.normaliserOptions());this.label_step=this.normaliser.step;this.range=this.normaliser.range;this.start_value=this.normaliser.start_value;this.zero_value=this.normaliser.zero_value;this.data_size=this.longestDataSetLength();if(c&&c.color)c.colors={},this.data_sets.keys().each(function(a){c.colors[a]=
10
+ c.color});this.options.colors=this.makeRandomColors();this.options.labels=$A($R(1,this.data_size));Object.extend(this.options,this.chartDefaults()||{});Object.extend(this.options,c||{});this.x_padding_left=10+this.paddingLeftOffset();this.x_padding_left+=this.options.vertical_label_unit?6:0;this.x_padding_left=this.options.left_padding?this.options.left_padding:this.x_padding_left;this.x_padding_right=this.options.right_padding||this.paddingRightOffset();this.x_padding=this.x_padding_left+this.x_padding_right;
11
+ this.y_padding_top=this.options.padding_top;this.y_padding_bottom=20+this.paddingBottomOffset();this.y_padding=this.y_padding_top+this.y_padding_bottom;this.graph_width=this.options.width-this.x_padding;this.graph_height=this.options.height-this.y_padding;this.step=this.calculateStep();this.y_label_count=(this.range/this.label_step).round();if(isNaN(this.y_label_count))this.y_label_count=1,this.options.show_vertical_labels=!1;this.normaliser.min+this.y_label_count*this.normaliser.step<this.normaliser.max&&
12
+ (this.y_label_count+=1);this.value_labels=this.makeValueLabels(this.y_label_count);this.top_value=this.value_labels.last();this.grid_start_offset=-1;this.paper=new Raphael(this.element,this.options.width,this.options.height);this.background=this.paper.rect(this.x_padding_left,this.y_padding_top,this.graph_width,this.graph_height);this.background.attr({fill:this.options.background_color,stroke:"none"});this.options.meanline=this.options.meanline===!0?{"stroke-width":"2px",stroke:"#BBBBBB"}:this.options.meanline;
13
+ this.globalMarkerSet=this.paper.set();this.globalHoverSet=this.paper.set();this.globalBlockSet=this.paper.set();this.globalAreaLineSet=this.paper.set();this.setChartSpecificOptions();this.draw();this.globalAreaLineSet.toFront();this.globalMarkerSet.toFront();this.globalHoverSet.toFront();this.globalBlockSet.toFront()},normaliserOptions:function(){return{graph_height:parseInt(this.element.getStyle("height"),10)}},hasBaseLine:function(){return!1},getNormalizedBaseLine:function(){if(this.normalized_base_line==
14
+ void 0)this.normalized_base_line=this.normaliseData(this.base_line);return this.normalized_base_line},getNormalizedRealData:function(){if(this.normalized_real_data==void 0)this.normalized_real_data=this.real_data.collect(function(a){return this.normaliseData(a[1])}.bind(this));return this.normalized_real_data},chartDefaults:function(){},drawPlot:function(){},calculateStep:function(){},getMousePos:function(a){var b=0,c=0;if(!a)a=window.event;if(a.pageX||a.pageY)b=a.pageX,c=a.pageY;else if(a.clientX||
15
+ a.clientY)b=a.clientX+document.body.scrollLeft-document.documentElement.scrollLeft,c=a.clientY+document.body.scrollTop-document.documentElement.scrollTop;return{x:b,y:c}},makeRandomColors:function(){var a={},b=1/this.data_sets.size(),c=Math.random();this.data_sets.each(function(d){a[d[0]]=Raphael.hsb2rgb(c,0.85,0.75).hex;c=(c+b)%1});return a},longestDataSetLength:function(){var a=0;this.data_sets.each(function(b){a=b[1].length>a?b[1].length:a});return a},roundValue:function(a,b){var c=Math.pow(10,
16
+ b);a*=c;return a=Math.round(a)/c},roundValues:function(a,b){return $A(a).collect(function(a){return this.roundValue(a,b)}.bind(this))},paddingLeftOffset:function(){var a=0;if(this.options.show_vertical_labels)a=this.flat_data,a=this.roundValues(a,2),a=$A(a).max(function(a){return a.toString().length}),a=(a>2?a-1:a)*this.options.font_size;return a},paddingRightOffset:function(){return 20},paddingBottomOffset:function(){return this.options.font_size},normalise:function(a){var b=this.start_value===0?
17
+ this.top_value:this.range;b===0&&(b=1);return a/b*this.graph_height},draw:function(){this.options.grid&&this.drawGrid();this.options.watermark&&this.drawWatermark();this.options.show_vertical_labels&&this.drawVerticalLabels();this.options.show_horizontal_labels&&this.drawHorizontalLabels();this.options.watermark||this.drawLinesInit(this);this.options.draw_axis&&this.drawAxis();this.start_value!==0&&this.options.focus_hint&&this.drawFocusHint();this.options.meanline&&this.drawMeanLine(this.normaliseData(this.flat_data))},
18
+ drawLinesInit:function(a){a.data_sets.each(function(b,c){a.drawLines(b[0],a.options.colors[b[0]],a.normaliseData(b[1]),a.options.datalabels[b[0]],a.element,c)}.bind(a))},drawWatermark:function(){var a=this.options.watermark,b=new Image,c=this;b.onload=function(){var a,e;c.options.watermark_location==="middle"?(a=(c.graph_width-b.width)/2+c.x_padding_left,e=(c.graph_height-b.height)/2+c.y_padding_top):(a=c.graph_width-b.width+c.x_padding_left-2,e=c.graph_height-b.height+c.y_padding_top-2);a=c.paper.image(b.src,
19
+ a,e,b.width,b.height).attr({opacity:"0.4"});c.drawLinesInit(c,c.data);(c.options.stacked_fill||c.options.area)&&a.toFront()};b.src=a.src||a},drawGrid:function(){var a=this.paper.path().attr({stroke:this.options.grid_color}),b,c,d;if(this.options.show_horizontal_grid){b=this.graph_height+this.y_padding_top;for(d=0;d<this.y_label_count+1;d++)a.moveTo(this.x_padding_left-0.5,parseInt(b,10)+0.5),a.lineTo(this.x_padding_left+this.graph_width-0.5,parseInt(b,10)+0.5),b-=this.graph_height/this.y_label_count}if(this.options.show_vertical_grid){b=
20
+ this.x_padding_left+this.options.plot_padding+this.grid_start_offset;c=this.options.labels.length;for(d=0;d<c;d++){if(this.options.hide_empty_label_grid===!0&&this.options.labels[d]!==""||this.options.hide_empty_label_grid===!1)a.moveTo(parseInt(b,10),this.y_padding_top),a.lineTo(parseInt(b,10),this.y_padding_top+this.graph_height);b+=this.step}}},drawLines:function(a,b,c,d,e,f){var g=this.calculateCoords(c),h=this.graph_height+this.y_padding_top,i,j,k;if(this.options.start_at_zero===!1)k=0,$A(g).each(function(a){if(a[1]===
21
+ h)k++;else throw $break;}),this.options.odd_horizontal_offset=k,this.options.odd_horizontal_offset>1&&g.splice(0,this.options.odd_horizontal_offset);this.options.stacked_fill||this.options.area?(this.options.area?(a=this.options.area_opacity?this.options.area_opacity:1.5/this.data_sets.collect(function(a){return a.length}).length,i=this.paper.path().attr({stroke:b,fill:b,"stroke-width":"0",opacity:a,"stroke-opacity":0})):i=this.paper.path().attr({stroke:b,fill:b,"stroke-width":"0"}),this.hasBaseLine()||
22
+ (g.unshift([g[0][0],h]),g.push([g[g.length-1][0],h]))):i=this.paper.path().attr({stroke:b,"stroke-width":this.options.stroke_width+"px"});$A(g).each(function(a,c){var h=a[0],j=a[1];b instanceof Array?this.drawPlot(c,i,h,j,b[c%b.length],g,d,e,f):this.drawPlot(c,i,h,j,b,g,d,e,f)}.bind(this));this.options.area&&this.options.stroke_width>0&&(j=this.paper.path().attr({stroke:b,"stroke-width":this.options.stroke_width+"px"}),g.remove(0),g.remove(-1),$A(g).each(function(a,c){this.drawPlot(c,j,a[0],a[1],
23
+ b,g,d,e,f,!0)}.bind(this)),this.globalAreaLineSet.push(j));this.options.datalabels&&this.options.draw_hovers&&(this.drawHover(i,d,e,b),this.globalHoverSet.toFront())},calculateCoords:function(a){var b=this.x_padding_left+this.options.plot_padding-this.step,c=this.graph_height+this.y_padding_top+this.normalise(this.start_value);a=$A(a).collect(function(a){b+=this.step;return[b,c-a]}.bind(this));if(!this.hasBaseLine())return a;b+=this.step;for(var d=this.getNormalizedBaseLine(),e=d.length-1;e>=0;e--)b-=
24
+ this.step,a.push([b,c-d[e]]);return a},drawFocusHint:function(){var a=this.x_padding_left+2.5-1,b=this.options.height-this.y_padding_bottom,c=this.paper.path().attr({stroke:this.options.label_color,"stroke-width":2});c.moveTo(a,b);c.lineTo(a-5,b-5);c.moveTo(a,b-5);c.lineTo(a-5,b-10)},drawMeanLine:function(a){var b=this.paper.path().attr(this.options.meanline);a=$A(a).inject(0,function(a,b){return b+a})/a.length-0.5;a=this.options.bar?a+this.zero_value*(this.graph_height/this.y_label_count):a;b.moveTo(this.x_padding_left-
25
+ 1,this.options.height-this.y_padding_bottom-a).lineTo(this.graph_width+this.x_padding_left,this.options.height-this.y_padding_bottom-a)},drawAxis:function(){var a=this.paper.path().attr({stroke:this.options.label_color});a.moveTo(parseInt(this.x_padding_left,10)-0.5,this.options.height-parseInt(this.y_padding_bottom,10)+0.5);a.lineTo(parseInt(this.graph_width+this.x_padding_left,10)-0.5,this.options.height-parseInt(this.y_padding_bottom,10)+0.5);a.moveTo(parseInt(this.x_padding_left,10)-0.5,parseInt(this.options.height-
26
+ this.y_padding_bottom,10)+0.5);a.lineTo(parseInt(this.x_padding_left,10)-0.5,parseInt(this.y_padding_top,10))},makeValueLabels:function(a){for(var b=this.label_step,c=this.start_value,d=[],e=0;e<a;e++)c=this.roundValue(c+b,3),d.push(c);return d},drawMarkers:function(a,b,c,d,e,f){var g=parseInt(this.x_padding_left,10)-0.5+d*b[0],h=this.options.height-this.y_padding_bottom+d*b[1],i=this.paper.path().attr({stroke:this.options.label_color}),j={font:this.options.font_size+'px "Arial"',stroke:"none",fill:this.options.label_color};
27
+ Object.extend(j,f||{});a.each(function(a){if(this.options.draw_axis&&(this.options.hide_empty_label_grid===!0&&a!==""||this.options.hide_empty_label_grid===!1)&&this.options.show_ticks)i.moveTo(parseInt(g,10),parseInt(h,10)+0.5),i.lineTo(parseInt(g,10)+5*b[1],parseInt(h,10)+0.5+5*b[0]);a!==""&&this.paper.text(g+e[0],h-2-e[1],a.toString()).attr(j);g+=c*b[0];h+=c*b[1]}.bind(this))},drawVerticalLabels:function(){for(var a=this.graph_height/this.y_label_count,b=this.options.vertical_label_unit?" "+this.options.vertical_label_unit:
28
+ "",c=0;c<this.value_labels.length;c++)this.value_labels[c]+=b;this.drawMarkers(this.value_labels,[0,-1],a,a,[-8,-2],{"text-anchor":"end"})},drawHorizontalLabels:function(){var a=this.options.label_rotation?{rotation:this.options.label_rotation,translation:-this.options.font_size+" 0"}:{},b=this.options.labels;if(this.options.label_max_size)for(var c=0;c<b.length;c++)b[c]=b[c].truncate(this.options.label_max_size+1,"\u2026");this.drawMarkers(b,[1,0],this.step,this.options.plot_padding,[0,(this.options.font_size+
29
+ 7)*-1],a)},drawHover:function(a,b,c,d){var e=this,f=this.options.stacked_fill||this.options.area?"fill":"stroke",g=this.options.hover_color||d,h=this.paper.set();b=this.paper.text(a.attrs.x,a.attrs.y-this.options.font_size*1.5-4,b).attr({"font-size":this.options.font_size,fill:this.options.hover_text_color,opacity:1});var i=b.getBBox(),j=this.drawRoundRect(b,i,4);h.push(j,b).attr({opacity:0});this.checkHoverPos({rect:j,set:h});this.globalHoverSet.push(h);a.hover(function(b){f==="fill"?a.animate({fill:g,
30
+ stroke:g},200):a.animate({stroke:g},200);b=e.getMousePos(b);h[0].attr({x:b.x-i.width/2-4-c.offsetLeft,y:b.y-i.height/2-e.options.font_size*1.5-4-c.offsetTop,opacity:1});h[1].attr({x:b.x-c.offsetLeft,y:b.y-e.options.font_size*1.5-c.offsetTop,opacity:1});a.mousemove(function(a){a=e.getMousePos(a);h[0].attr({x:a.x-i.width/2-4-c.offsetLeft,y:a.y-i.height/2-e.options.font_size*1.5-4-c.offsetTop,opacity:1});h[1].attr({x:a.x-c.offsetLeft,y:a.y-e.options.font_size*1.5-c.offsetTop,opacity:1});e.checkHoverPos(j,
31
+ h)})},function(){f==="fill"?a.animate({fill:d,stroke:d},200):a.animate({stroke:d},200);h.attr({opacity:0})})},checkHoverPos:function(a){var b,c,d,e,f,g;if(a.rect)c=a.rect,d=c.getBBox();if(a.set)e=a.set;if(a.marker)f=a.marker;if(a.nib)g=a.nib;if(a.textpadding)b=a.textpadding;if(c&&e){if(c.attrs.y<0)g&&f?(a=e.getBBox(),e.translate(0,a.height+b*2),f.translate(0,-a.height-b*2),g.translate(0,-d.height-b+1.5).scale(1,-1)):(b=c.attrs.y,e.translate(0,1-b));c.attrs.y+d.height>this.options.height&&(b=c.attrs.y+
32
+ d.height-this.options.height,e.translate(0,-b-1),f&&f.translate(0,b+1));if(c.attrs.x<0)b=c.attrs.x,e.translate(-b+1,0),g&&g.translate(b-1,0),f&&f.translate(b-1,0);c.attrs.x+d.width>this.options.width&&(b=c.attrs.x+d.width-this.options.width,e.translate(-b-1,0),g&&g.translate(b+1,0),f&&f.translate(b+1,0))}},drawNib:function(a,b,c){return this.paper.path().attr({fill:this.options.label_color,opacity:1,stroke:this.options.label_color,"stroke-width":"0px"}).moveTo(a.attrs.x-c,a.attrs.y+b.height/2+c-1).lineTo(a.attrs.x,
33
+ a.attrs.y+b.height/2+c*2).lineTo(a.attrs.x+c,a.attrs.y+b.height/2+c-1).andClose()},drawRoundRect:function(a,b,c){return this.paper.rect(a.attrs.x-b.width/2-c,a.attrs.y-b.height/2-c,b.width+c*2,b.height+c*2,c*1.5).attr({fill:this.options.label_color,opacity:1,stroke:this.options.label_color,"stroke-width":"0px"})}});Array.prototype.sum=function(){for(var a=0,b=0;a<this.length;b+=this[a++]);return b};
34
+ if(typeof Array.prototype.max==="undefined")Array.prototype.max=function(){return Math.max.apply({},this)};if(typeof Array.prototype.min==="undefined")Array.prototype.min=function(){return Math.min.apply({},this)};Array.prototype.mean=function(){return this.sum()/this.length};Array.prototype.variance=function(){for(var a=this.mean(),b=0,c=0;c<this.length;c++)b+=Math.pow(this[c]-a,2);return b/(this.length-1)};Array.prototype.standard_deviation=function(){return Math.sqrt(this.variance())};
35
+ Array.prototype.remove=function(a,b){var c=this.slice((b||a)+1||this.length);this.length=a<0?this.length+a:a;return this.push.apply(this,c)};Raphael.el.isAbsolute=!0;Raphael.el.absolutely=function(){this.isAbsolute=1;return this};Raphael.el.relatively=function(){this.isAbsolute=0;return this};Raphael.el.moveTo=function(a,b){this._last={x:a,y:b};return this.attr({path:this.attrs.path+["m","M"][+this.isAbsolute]+parseFloat(a)+" "+parseFloat(b)})};
36
+ Raphael.el.lineTo=function(a,b){this._last={x:a,y:b};return this.attr({path:this.attrs.path+["l","L"][+this.isAbsolute]+parseFloat(a)+" "+parseFloat(b)})};Raphael.el.cplineTo=function(a,b,c){a>this._last.x?this.attr({path:this.attrs.path+["C",this._last.x+c,this._last.y,a-c,b,a,b]}):a==this._last.x?this.lineTo(a,b):this.attr({path:this.attrs.path+["C",this._last.x-c,this._last.y,a+c,b,a,b]});this._last={x:a,y:b};return this};Raphael.el.andClose=function(){return this.attr({path:this.attrs.path+"z"})};
37
+ Grafico.LineGraph=Class.create(Grafico.BaseGraph,{chartDefaults:function(){return{line:!0,start_at_zero:!0,stroke_width:5,curve_amount:10}},setChartSpecificOptions:function(){},calculateStep:function(){return(this.graph_width-this.options.plot_padding*2)/(this.data_size-1)},startPlot:function(a,b,c){a.moveTo(b,c)},drawPlot:function(a,b,c,d,e,f,g,h,i){if(a===0)return this.startPlot(b,c-0.5,d,e);this.options.curve_amount?b.cplineTo(c,d,this.options.curve_amount):b.lineTo(c,d);this.options.markers===
38
+ "circle"?this.drawGraphMarkers(a,c,d,e,g,h):this.options.markers==="value"&&this.drawGraphValueMarkers(a,c,d,e,g,h,i)},drawGraphMarkers:function(a,b,c,d){var e=this.paper.circle(b,c,this.options.marker_size),f=this.options.marker_size,g=this.options.hover_color||d,h=parseInt(1.7*f,10);e.attr({"stroke-width":"1px",stroke:this.options.background_color,fill:d});this.globalMarkerSet.push(e);e.hover(function(){e.animate({r:h,fill:g},200)},function(){e.animate({r:f,fill:d},200)})},drawGraphValueMarkers:function(a,
39
+ b,c,d,e,f,g){a+=this.options.odd_horizontal_offset>1?this.options.odd_horizontal_offset:0;a-=this.options.stacked_fill||this.options.area?1:0;if(f=(this.options.stacked?this.real_data:this.data_sets).collect(function(b){return b[1][a]})[g])f=""+f.toString().split("."),f[1]&&(f[1]=f[1].truncate(3,""));if(this.options.line||this.options.stacked||(this.options.stacked_fill||this.options.area)&&a!=-1&&typeof f!="undefined"){g=b-this.step/2;var h=c-[this.options.stroke_width/2,this.options.hover_radius].max(),
40
+ i=this.step,j=[this.options.stroke_width,this.options.hover_radius*2].max();b=this.paper.circle(b,c,this.options.marker_size==0?[this.options.stroke_width*1.5,this.step].min():this.options.marker_size).attr({"stroke-width":"1px",stroke:this.options.background_color,fill:d,opacity:0});d=this.paper.rect(g,h,i,j).attr({fill:d,"stroke-width":0,stroke:d,opacity:0});e=this.options.datalabels?typeof e=="function"?e.call(this,a,f):e+": "+f:""+f;e+=this.options.vertical_label_unit?" "+this.options.vertical_label_unit:
41
+ "";var k=this.paper.set();e=this.paper.text(b.attrs.cx,b.attrs.cy-this.options.font_size*1.5-8,e).attr({"font-size":this.options.font_size,fill:this.options.hover_text_color,opacity:1});f=e.getBBox();c=this.drawRoundRect(e,f,4);f=this.drawNib(e,f,4);k.push(b,c,f,e).attr({opacity:0});this.checkHoverPos({rect:c,set:k,marker:b,nib:f,textpadding:4});this.globalHoverSet.push(k);this.globalBlockSet.push(d);d.hover(function(){k.animate({opacity:1},200)},function(){k.animate({opacity:0},200)})}}});
42
+ Grafico.AreaGraph=Class.create(Grafico.LineGraph,{chartDefaults:function(){return{area:!0,area_opacity:!1,stroke_width:0,curve_amount:10}},drawPlot:function(a,b,c,d,e,f,g,h,i,j){var k=this.options.area||this.options.stacked_fill;j||(k===!0?a!==0&&a!==f.length-1&&(this.options.markers==="circle"?this.drawGraphMarkers(a,c,d,e,g,h):this.options.markers==="value"&&this.drawGraphValueMarkers(a,c,d,e,g,h,i)):this.options.markers==="circle"?this.drawGraphMarkers(a,c,d,e,g,h):this.options.markers==="value"&&
43
+ this.drawGraphValueMarkers(a,c,d,e,g,h,i));c-=0.5;if(a===0)return this.startPlot(b,c,d,e);this.options.curve_amount&&a>1&&a<f.length-1?b.cplineTo(c,d,this.options.curve_amount):this.options.curve_amount&&!k?b.cplineTo(c,d,this.options.curve_amount):b.lineTo(c,d)}});
44
+ Grafico.StackGraph=Class.create(Grafico.AreaGraph,{chartDefaults:function(){return{stacked:!0,stacked_fill:!0,stroke_width:0,curve_amount:10}},setChartSpecificOptions:function(){if(!this.options.stacked_fill)this.options.stroke_width=5},stackData:function(a){for(var b=a.collect(function(a){return a[1]}),c=b.length-2;c>=0;c--)for(var d=0;d<b[0].length;d++)b[c][d]+=b[c+1][d];return a}});
45
+ Grafico.StreamGraph=Class.create(Grafico.StackGraph,{chartDefaults:function(){return{stacked:!0,stacked_fill:!0,stroke_width:5,grid:!1,draw_axis:!1,show_horizontal_labels:!1,show_vertical_labels:!1,stream:!0,stream_line_smoothing:!1,stream_smart_insertion:!1,curve_amount:4,stream_label_threshold:0}},hasBaseLine:function(){return!0},calcBaseLine:function(a){var b=a.collect(function(a){return a[1]});a=[];var c,d;for(d=0;d<b[0].length;d++){for(c=sum=0;c<b.length;c++)sum+=this.options.stream_line_smoothing==
46
+ !1?b[c][d]:(c+1)*b[c][d];a[d]=this.options.stream_line_smoothing==!1?-sum/2:-sum/(b.length+1)}b=a.min();for(c=0;c<a.length;c++)a[c]-=b;this.base_line=a},stackData:function(a){var b,c;if(this.options.stream_smart_insertion){a=$A(a);a.each(function(a){for(b=0;b<a[1].length&&a[1][b]<=1.0E-7;)b++;a[2]=b});var d=[],e=!1;a.sortBy(function(a){return a[2]}).each(function(a){e?d.push(a):d.unshift(a);e=!e});a=$H();d.each(function(b){a.set(b[0],b[1])})}this.real_data=this.deepCopy(a);this.calcBaseLine(a);var f=
47
+ a.collect(function(a){return a[1]});for(c=0;c<f[0].length;c++)f[f.length-1][c]+=this.base_line[c];for(b=f.length-2;b>=0;b--)for(c=0;c<f[0].length;c++)f[b][c]+=f[b+1][c];return a},drawPlot:function(a,b,c,d,e,f,g,h,i,j){if(this.options.datalabels&&!j){j=this.getNormalizedRealData();var k=this.bestMarkerPositions();a<f.length/2&&k[i]>=a&&k[i]<a+1&&this.drawStreamMarker(a+1,c+(k[i]-a)*this.step,d+j[i][a]/2,e,g,h)}c-=0.5;if(a===0)return this.startPlot(b,c,d,e);b.cplineTo(c,d,this.options.curve_amount)},
48
+ bestMarkerPositions:function(){if(this.best_marker_positions==void 0)this.best_marker_positions=this.real_data.collect(function(a){for(var b=-1,c=0,d=0,e=0;e<a[1].length;e++){var f=a[1][e];f>c&&f>=this.options.stream_label_threshold?(c=f,b=e,d=0):f==c&&c>0&&d++}return b+d/2}.bind(this));return this.best_marker_positions},drawStreamMarker:function(a,b,c,d,e){this.options.datalabels&&(a=this.paper.set(),b=this.paper.text(b,c-8+this.options.font_size/2,e).attr({"font-size":this.options.font_size,fill:this.options.hover_text_color,
49
+ opacity:1}),c=b.getBBox(),c=this.drawRoundRect(b,c,4),a.push(c,b),this.checkHoverPos({rect:c,set:a,textpadding:4}),this.globalHoverSet.push(a))}});
50
+ Grafico.BarGraph=Class.create(Grafico.BaseGraph,{chartDefaults:function(){return{bar:!0,plot_padding:0,bargraph_lastcolor:!1,bargraph_negativecolor:!1}},normaliserOptions:function(){return{start_value:0,bar:!0}},setChartSpecificOptions:function(){this.bar_padding=5;this.bar_width=this.calculateBarWidth();this.options.plot_padding=this.bar_width/2;this.step=this.calculateStep();this.grid_start_offset=this.bar_padding-1},calculateBarWidth:function(){return this.graph_width/this.data_size-this.bar_padding},
51
+ calculateStep:function(){this.data_size=this.data_size==1?2:this.data_size;return(this.graph_width-this.options.plot_padding*2-this.bar_padding*2)/(this.data_size-1)},longestDataSetLength:function(){var a=0;this.data_sets.each(function(b){a=b[1].length>a?b[1].length:a});return a},drawPlot:function(a,b,c,d,e,f,g){b=this.options.height-this.y_padding_bottom-this.zero_value*(this.graph_height/this.y_label_count);var h=this.options.bargraph_lastcolor,i=this.options.bargraph_negativecolor||e;c+=this.bar_padding;
52
+ d=b-d;e=h&&a===f.length-1?h:d<0?i:e;f=this.paper.rect(c-this.bar_width/2,b,this.bar_width,d).attr({fill:e,"stroke-width":this.options.stroke_width,stroke:e,"stroke-opacity":0});d<0?f.attr({height:-f.attrs.height}):f.translate(0,-d);this.options.datalabels&&this.drawGraphValueMarkers(c,a,f,g,e);this.options.count++},drawHorizontalLabels:function(){var a=this.bar_padding+this.options.plot_padding,b=this.options.label_rotation?{rotation:this.options.label_rotation,translation:-this.options.font_size+
53
+ " 0"}:{},c=this.options.labels;if(this.options.label_max_size)for(var d=0;d<c.length;d++)c[d]=c[d].truncate(this.options.label_max_size+1,"\u2026");this.drawMarkers(this.options.labels,[1,0],this.step,a,[0,(this.options.font_size+7)*-1],b)},drawGrid:function(){var a=this.paper.path().attr({stroke:this.options.grid_color,"stroke-width":1}),b=this.graph_height+this.y_padding_top,c,d;if(this.options.horizontalbar)a.moveTo(this.x_padding_left-0.5,parseInt(b,10)+0.5),a.lineTo(this.x_padding_left+this.graph_width-
54
+ 0.5,parseInt(b,10)+0.5),b-=this.graph_height,a.moveTo(this.x_padding_left-0.5,parseInt(b,10)+0.5),a.lineTo(this.x_padding_left+this.graph_width-0.5,parseInt(b,10)+0.5);else if(this.options.show_horizontal_grid)for(var e=0;e<this.y_label_count+1;e++)a.moveTo(this.x_padding_left-0.5,parseInt(b,10)+0.5),a.lineTo(this.x_padding_left+this.graph_width-0.5,parseInt(b,10)+0.5),b-=this.graph_height/this.y_label_count;if(this.options.horizontalbar){b=this.x_padding_left+this.options.plot_padding+this.grid_start_offset;
55
+ c=this.y_label_count;d=this.options.horizontalbar?this.graph_width/this.y_label_count:this.step;for(e=0;e<c;e++){if(this.options.hide_empty_label_grid===!0&&this.options.labels[e]!==""||this.options.hide_empty_label_grid===!1)a.moveTo(parseInt(b,10)+0.5,this.y_padding_top),a.lineTo(parseInt(b,10)+0.5,this.y_padding_top+this.graph_height);b+=d}}a.moveTo(parseInt(this.x_padding_left,10)-0.5,this.y_padding_top);a.lineTo(parseInt(this.x_padding_left,10)-0.5,this.y_padding_top+this.graph_height);a.moveTo(parseInt(this.x_padding_left+
56
+ this.graph_width,10)-0.5,this.y_padding_top);a.lineTo(parseInt(this.x_padding_left+this.graph_width,10)-0.5,this.y_padding_top+this.graph_height)},drawGraphValueMarkers:function(a,b,c,d,e){var f=this.options.hover_color||e,g=this.paper.set();a=this.paper.rect(a-this.bar_width/2,this.y_padding_top,this.bar_width,this.options.height).attr({fill:e,"stroke-width":0,stroke:e,opacity:0});if(d=d&&d[b]?d[b].toString():""){b=this.paper.text(c.attrs.x+this.bar_width/2,c.attrs.y-this.options.font_size*1.5,d);
57
+ b.attr({"font-size":this.options.font_size,fill:this.options.hover_text_color,opacity:1});var h=b.getBBox();d=this.drawRoundRect(b,h,4);h=this.drawNib(b,h,4);g.push(d,h,b).attr({opacity:0});this.checkHoverPos({rect:d,set:g,nib:h});this.globalHoverSet.push(g);this.globalBlockSet.push(a);d.attrs.y<0&&g.translate(0,1+d.attrs.y*-1);a.hover(function(){c.animate({fill:f,stroke:f},200);g.animate({opacity:1},200)},function(){c.animate({fill:e,stroke:e},200);g.animate({opacity:0},200)})}}});
58
+ Grafico.StackedBarGraph=Class.create(Grafico.BarGraph,{chartDefaults:function(){return{opacity:100,bar:!0,plot_padding:0,stacked:!0,stacked_fill:!0}},setChartSpecificOptions:function(){this.bar_padding=5;this.bar_width=this.calculateBarWidth();this.options.plot_padding=this.bar_width/2;this.step=this.calculateStep();this.grid_start_offset=this.bar_padding-1;this.options.stroke_width=1},stackData:function(a){for(var b=a.collect(function(a){return a[1]}),c=b.length-2;c>=0;c--)for(var d=0;d<b[0].length;d++)b[c][d]+=
59
+ b[c+1][d];return a},calculateCoords:function(a){var b=this.x_padding_left+this.options.plot_padding-this.step,c=this.graph_height+this.y_padding_top+this.normalise(this.start_value);a=$A(a).collect(function(a){b+=this.step;return[b,c-a]}.bind(this));b+=this.step;for(var d=this.getNormalizedBaseLine(),e=d.length-1;e>=0;e--)b-=this.step,a.push([b,c-d[e]]);return a},drawLines:function(a,b,c,d,e,f){var g=this.calculateCoords(c),h;h=this.paper.path().attr({stroke:b,"stroke-width":0});$A(g).each(function(a,
60
+ c){this.drawPlot(c,h,a[0],a[1],b,g,d,e,f)}.bind(this));this.options.datalabels&&this.options.draw_hovers&&(this.drawHover(h,d,e,b),this.globalHoverSet.toFront())},drawPlot:function(a,b,c,d,e,f,g){b=this.options.height-this.y_padding_bottom-this.zero_value*(this.graph_height/this.y_label_count);c+=this.bar_padding;d=this.options.height-this.y_padding_bottom-d-this.zero_value*(this.graph_height/this.y_label_count);f={fill:e,"stroke-width":0,stroke:this.options.color2,opacity:this.options.opacity};b=
61
+ this.paper.rect(c-this.bar_width/2,b,this.bar_width,d).attr(f);d<0?b.attr({height:-b.attrs.height}):b.translate(0,-d);this.options.datalabels&&this.drawGraphValueMarkers(c,a,b,g,e);this.options.count++},drawGraphValueMarkers:function(a,b,c,d,e){a-=this.bar_width/2;var f=c.attrs.y,g=this.bar_width,h=c.attrs.height,i=this.options.hover_color||e,j=this.paper.set();a=this.paper.rect(a,f,g,h);d=d&&d.length>b?d[b].toString():"";b=this.paper.text(c.attrs.x+this.bar_width/2,c.attrs.y-this.options.font_size*
62
+ 1.5,d);a.attr({fill:e,"stroke-width":0,stroke:e,opacity:0});b.attr({"font-size":this.options.font_size,fill:this.options.hover_text_color,opacity:1});f=b.getBBox();d=this.drawRoundRect(b,f,4);f=this.drawNib(b,f,4);j.push(d,f,b).attr({opacity:0});this.checkHoverPos({rect:d,set:j,nib:f});this.globalHoverSet.push(j);this.globalBlockSet.push(a);d.attrs.y<0&&j.translate(0,1+d.attrs.y*-1);a.hover(function(){c.animate({fill:i},200);j.animate({opacity:1},200)},function(){c.animate({fill:e},200);j.animate({opacity:0},
63
+ 200)})}});
64
+ Grafico.HorizontalBarGraph=Class.create(Grafico.BarGraph,{chartDefaults:function(){return{bar:!0,horizontalbar:!0,plot_padding:0,horizontal_rounded:!1,bargraph_lastcolor:!1}},setChartSpecificOptions:function(){this.x_padding_left=20+this.longestLabel()*(this.options.font_size/2);this.bar_padding=5;this.bar_width=this.calculateBarHeight();this.step=this.calculateStep();this.graph_width=this.options.width-this.x_padding_right-this.x_padding_left},normalise:function(a){var b=this.makeValueLabels(this.y_label_count);b=
65
+ b[b.length-1];return a/b*this.graph_width},longestLabel:function(){return $A(this.options.labels).sort(function(a,b){return a.toString().length<b.toString().length}).first().toString().length},calculateBarHeight:function(){return this.graph_height/this.data_size-this.bar_padding},calculateStep:function(){return(this.graph_height-this.options.plot_padding*2)/this.data_size},drawLines:function(a,b,c,d){var e=this.y_padding_top+this.bar_padding/2-0.5,f=this.zero_value*(this.graph_width/this.y_label_count),
66
+ g=this.x_padding_left+f-0.5,h=this.options.bargraph_lastcolor,i=this.options.bargraph_negativecolor||b;this.datalabel=d;$A(c).each(function(a,d){var l,q=this.options.horizontal_rounded?this.bar_width/2:0,n;l=h&&d===c.length-1?h:a<0?i:b;a=a/this.graph_width*(this.graph_width-f);n=this.paper.rect(g,e,a,this.bar_width,q).attr({fill:l,"stroke-width":0,stroke:l,"stroke-opacity":0});a<0&&n.attr({width:-n.attrs.width}).translate(a,0);if(q){var t=this.paper.set(),m=this.paper.rect(g,e,a-this.bar_width/2,
67
+ this.bar_width).attr({fill:l,"stroke-width":0,stroke:l,"stroke-opacity":0});t.push(m,n);a<0&&m.attr({width:-m.attrs.width-this.bar_width}).translate(a+this.bar_width/2,0)}if(this.options.datalabels){var r=this.options.hover_color||l,o=this.paper.set();m=this.paper.text(f+a+this.x_padding_left/2,n.attrs.y-this.options.font_size*1.5,this.datalabel[d].toString()).attr({"font-size":this.options.font_size,fill:this.options.hover_text_color,opacity:1});var u=this.paper.rect(this.x_padding_left,e,this.graph_width,
68
+ this.bar_width).attr({fill:l,"stroke-width":0,stroke:l,opacity:0}),p=m.getBBox();a<0&&m.translate(p.width,0);var s=this.drawRoundRect(m,p,4);p=this.drawNib(m,p,4);o.push(s,p,m).attr({opacity:0});this.checkHoverPos({rect:s,set:o,nib:p});this.globalHoverSet.push(o);this.globalBlockSet.push(u);s.attrs.y<0&&o.translate(0,1-s.attrs.y);u.hover(function(){q?t.animate({fill:r,stroke:r},200):n.animate({fill:r,stroke:r},200);o.animate({opacity:1},200)},function(){q?t.animate({fill:l,stroke:l},200):n.animate({fill:l,
69
+ stroke:l},200);o.animate({opacity:0},200)})}e+=this.step}.bind(this))},drawFocusHint:function(){var a=this.x_padding_left+10,b=this.options.height-this.y_padding_bottom-2.5;this.paper.path().attr({stroke:this.options.label_color,"stroke-width":2}).moveTo(a,b).lineTo(a-5,b+5).moveTo(a-5,b).lineTo(a-10,b+5)},drawVerticalLabels:function(){var a=this.step/2,b=this.options.label_rotation?{"text-anchor":"end",rotation:this.options.label_rotation,translation:"0 "+this.options.font_size/2}:{"text-anchor":"end"},
70
+ c=this.options.labels;if(this.options.label_max_size)for(var d=0;d<c.length;d++)c[d]=c[d].truncate(this.options.label_max_size+1,"\u2026");this.drawMarkers(this.options.labels.reverse(),[0,-1],this.step,a,[-8,-(this.options.font_size/5)],b)},drawHorizontalLabels:function(){var a=this.graph_width/this.y_label_count,b=this.makeValueLabels(this.y_label_count);if(this.options.vertical_label_unit)for(var c=0;c<b.length;c++)b[c]+=this.options.vertical_label_unit;this.drawMarkers(b,[1,0],a,a,[0,(this.options.font_size+
71
+ 7)*-1])},drawMeanLine:function(a){a=$A(a).inject(0,function(a,c){return c+a})/a.length;a=this.options.bar?a+this.zero_value*(this.graph_height/this.y_label_count):a;this.paper.path().attr(this.options.meanline).moveTo(this.x_padding_left-1+a,this.y_padding_top).lineTo(this.x_padding_left-1+a,this.y_padding_top+this.graph_height)}});
72
+ Grafico.SparkLine=Class.create(Grafico.Base,{initialize:function(a,b,c){this.element=a;this.data=b;this.options={highlight:!1,stroke_width:1,color:this.makeRandomcolor(),width:parseInt(a.getStyle("width"),10),height:parseInt(a.getStyle("height"),10),acceptable_range:!1,zeroline:!1};Object.extend(this.options,c||{});this.step=this.calculateStep();this.paper=new Raphael(this.element,this.options.width,this.options.height);this.drawBackground();this.draw()},drawBackground:function(){this.background=
73
+ this.options.acceptable_range?this.paper.rect(0,this.options.height-this.normalise(this.options.acceptable_range[1]),this.options.width,this.normalise(this.options.acceptable_range[1])-this.normalise(this.options.acceptable_range[0])):this.paper.rect(0,0,this.options.width,this.options.height);this.background.attr({fill:this.options.background_color,stroke:"none"})},calculateStep:function(){return this.options.width/(this.data.length-1)},makeRandomcolor:function(){return Raphael.hsb2rgb(Math.random(),
74
+ 1,0.75).hex},normalise:function(a){var b=this.data.min()<0?this.data.max()-this.data.min():this.data.max();a-=this.data.min()<0?this.data.min():0;return a/b*this.options.height},draw:function(){var a=this.normaliseData(this.data),b;if(this.options.zeroline&&this.data.min()<0)this.options.zeroline=this.options.zeroline===!0?{"stroke-width":"1px",stroke:"#BBBBBB"}:this.options.zeroline,b=parseInt(this.options.height-this.normalise(0),10),this.paper.path().attr(this.options.zeroline).moveTo(0,b).lineTo(this.options.width,
75
+ b);this.drawLines(this.options.color,a);this.options.highlight&&this.showHighlight(a)},drawLines:function(a,b){var c=this.paper.path().attr({stroke:a,"stroke-width":this.options.stroke_width}).moveTo(0,this.options.height-b.first()),d=0,e=this.data.min()<0?this.options.stroke_width:0;b.slice(1).each(function(a){d+=this.step;c.lineTo(d,this.options.height-a-e)}.bind(this))},showHighlight:function(a){var b=1+this.options.stroke_width/2,c=this.options.width-b;a=a[this.options.highlight.index||a.length-
76
+ 1]+(b/2).round();var d=this.options.highlight.color||"#f00";typeof this.options.highlight.index!=="undefined"&&(c=this.step*this.options.highlight.index);this.paper.circle(c,this.options.height-a+b/2,b).attr({stroke:!1,fill:d})}});
77
+ Grafico.SparkBar=Class.create(Grafico.SparkLine,{calculateStep:function(){return this.options.width/this.data.length},drawLines:function(a,b){var c=this.options.bargraph_lastcolor,d=this.step>2?this.step-1:this.step,e=d/2,f=this.normalise(0);b.each(function(g,h){var i,j;i=c&&h===b.length-1?c:a;j=this.paper.path().attr({stroke:i,"stroke-width":d});j.moveTo(e,this.options.height-g);j.lineTo(e,this.options.height-f);g<f&&j.attr({stroke:this.options.bargraph_negativecolor||i});e+=this.step}.bind(this))},
78
+ showHighlight:function(){}});
79
+ Grafico.SparkArea=Class.create(Grafico.SparkLine,{drawLines:function(a,b){var c=a,d=a,e=0.2,f=this.normalise(0);typeof a=="object"&&(c=a[1],d=a[0],e=1);var g=this.paper.path().attr({fill:c,stroke:c,"stroke-width":0,"stroke-opacity":0,opacity:e}).moveTo(0,this.options.height-f).lineTo(0,this.options.height-b.first()),h=this.paper.path().attr({stroke:d,"stroke-width":this.options.stroke_width}).moveTo(0,this.options.height-b.first()),i=0;b.slice(1).each(function(a){i+=this.step;g.lineTo(i,this.options.height-
80
+ a);h.lineTo(i,this.options.height-a)}.bind(this));g.lineTo(i,this.options.height-f)}});
js/aitoc/aitloadmon/raphael-min.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ /*
2
+ * Raphael 1.5.2 - JavaScript Vector Library
3
+ *
4
+ * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com)
5
+ * Licensed under the MIT (http://raphaeljs.com/license.html) license.
6
+ */
7
+ (function(){function a(){if(a.is(arguments[0],G)){var b=arguments[0],d=bV[m](a,b.splice(0,3+a.is(b[0],E))),e=d.set();for(var g=0,h=b[w];g<h;g++){var i=b[g]||{};c[f](i.type)&&e[L](d[i.type]().attr(i))}return e}return bV[m](a,arguments)}a.version="1.5.2";var b=/[, ]+/,c={circle:1,rect:1,path:1,ellipse:1,text:1,image:1},d=/\{(\d+)\}/g,e="prototype",f="hasOwnProperty",g=document,h=window,i={was:Object[e][f].call(h,"Raphael"),is:h.Raphael},j=function(){this.customAttributes={}},k,l="appendChild",m="apply",n="concat",o="createTouch"in g,p="",q=" ",r=String,s="split",t="click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend orientationchange touchcancel gesturestart gesturechange gestureend"[s](q),u={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},v="join",w="length",x=r[e].toLowerCase,y=Math,z=y.max,A=y.min,B=y.abs,C=y.pow,D=y.PI,E="number",F="string",G="array",H="toString",I="fill",J=Object[e][H],K={},L="push",M=/^url\(['"]?([^\)]+?)['"]?\)$/i,N=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,O={"NaN":1,Infinity:1,"-Infinity":1},P=/^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,Q=y.round,R="setAttribute",S=parseFloat,T=parseInt,U=" progid:DXImageTransform.Microsoft",V=r[e].toUpperCase,W={blur:0,"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:"10px \"Arial\"","font-family":"\"Arial\"","font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/",opacity:1,path:"M0,0",r:0,rotation:0,rx:0,ry:0,scale:"1 1",src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",translation:"0 0",width:0,x:0,y:0},X={along:"along",blur:E,"clip-rect":"csv",cx:E,cy:E,fill:"colour","fill-opacity":E,"font-size":E,height:E,opacity:E,path:"path",r:E,rotation:"csv",rx:E,ry:E,scale:"csv",stroke:"colour","stroke-opacity":E,"stroke-width":E,translation:"csv",width:E,x:E,y:E},Y="replace",Z=/^(from|to|\d+%?)$/,$=/\s*,\s*/,_={hs:1,rg:1},ba=/,?([achlmqrstvxz]),?/gi,bb=/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,bc=/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,bd=/^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/,be=function(a,b){return a.key-b.key};a.type=h.SVGAngle||g.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML";if(a.type=="VML"){var bf=g.createElement("div"),bg;bf.innerHTML="<v:shape adj=\"1\"/>";bg=bf.firstChild;bg.style.behavior="url(#default#VML)";if(!(bg&&typeof bg.adj=="object"))return a.type=null;bf=null}a.svg=!(a.vml=a.type=="VML");j[e]=a[e];k=j[e];a._id=0;a._oid=0;a.fn={};a.is=function(a,b){b=x.call(b);if(b=="finite")return!O[f](+a);return b=="null"&&a===null||b==typeof a||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||J.call(a).slice(8,-1).toLowerCase()==b};a.angle=function(b,c,d,e,f,g){{if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return((h<0)*180+y.atan(-i/-h)*180/D+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)}};a.rad=function(a){return a%360*D/180};a.deg=function(a){return a*180/D%360};a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,G)){var e=b.length;while(e--)if(B(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(f<d)return c-f;if(f>b-d)return c-f+b}return c};function bh(){var a=[],b=0;for(;b<32;b++)a[b]=(~(~(y.random()*16)))[H](16);a[12]=4;a[16]=(a[16]&3|8)[H](16);return"r-"+a[v]("")}a.setWindow=function(a){h=a;g=h.document};var bi=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write("<body>");e.close();d=e.body}catch(a){d=createPopup().document.body}var f=d.createTextRange();bi=bm(function(a){try{d.style.color=r(a)[Y](c,p);var b=f.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b[H](16)).slice(-6)}catch(a){return"none"}})}else{var h=g.createElement("i");h.title="Rapha├лl Colour Picker";h.style.display="none";g.body[l](h);bi=bm(function(a){h.style.color=a;return g.defaultView.getComputedStyle(h,p).getPropertyValue("color")})}return bi(b)},bj=function(){return"hsb("+[this.h,this.s,this.b]+")"},bk=function(){return"hsl("+[this.h,this.s,this.l]+")"},bl=function(){return this.hex};a.hsb2rgb=function(b,c,d,e){if(a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b){d=b.b;c=b.s;b=b.h;e=b.o}return a.hsl2rgb(b,c,d/2,e)};a.hsl2rgb=function(b,c,d,e){if(a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b){d=b.l;c=b.s;b=b.h}if(b>1||c>1||d>1){b/=360;c/=100;d/=100}var f={},g=["r","g","b"],h,i,j,k,l,m;if(c){d<0.5?h=d*(1+c):h=d+c-d*c;i=2*d-h;for(var n=0;n<3;n++){j=b+1/3*-(n-1);j<0&&j++;j>1&&j--;j*6<1?f[g[n]]=i+(h-i)*6*j:j*2<1?f[g[n]]=h:j*3<2?f[g[n]]=i+(h-i)*(2/3-j)*6:f[g[n]]=i}}else f={r:d,g:d,b:d};f.r*=255;f.g*=255;f.b*=255;f.hex="#"+(16777216|f.b|f.g<<8|f.r<<16).toString(16).slice(1);a.is(e,"finite")&&(f.opacity=e);f.toString=bl;return f};a.rgb2hsb=function(b,c,d){if(c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b){d=b.b;c=b.g;b=b.r}if(c==null&&a.is(b,F)){var e=a.getRGB(b);b=e.r;c=e.g;d=e.b}if(b>1||c>1||d>1){b/=255;c/=255;d/=255}var f=z(b,c,d),g=A(b,c,d),h,i,j=f;{if(g==f)return{h:0,s:0,b:f,toString:bj};var k=f-g;i=k/f;b==f?h=(c-d)/k:c==f?h=2+(d-b)/k:h=4+(b-c)/k;h/=6;h<0&&h++;h>1&&h--}return{h:h,s:i,b:j,toString:bj}};a.rgb2hsl=function(b,c,d){if(c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b){d=b.b;c=b.g;b=b.r}if(c==null&&a.is(b,F)){var e=a.getRGB(b);b=e.r;c=e.g;d=e.b}if(b>1||c>1||d>1){b/=255;c/=255;d/=255}var f=z(b,c,d),g=A(b,c,d),h,i,j=(f+g)/2,k;if(g==f)k={h:0,s:0,l:j};else{var l=f-g;i=j<0.5?l/(f+g):l/(2-f-g);b==f?h=(c-d)/l:c==f?h=2+(d-b)/l:h=4+(b-c)/l;h/=6;h<0&&h++;h>1&&h--;k={h:h,s:i,l:j}}k.toString=bk;return k};a._path2string=function(){return this.join(",")[Y](ba,"$1")};function bm(a,b,c){function d(){var g=Array[e].slice.call(arguments,0),h=g[v]("тЦ║"),i=d.cache=d.cache||{},j=d.count=d.count||[];if(i[f](h))return c?c(i[h]):i[h];j[w]>=1000&&delete i[j.shift()];j[L](h);i[h]=a[m](b,g);return c?c(i[h]):i[h]}return d}a.getRGB=bm(function(b){if(!b||!(!((b=r(b)).indexOf("-")+1)))return{r:-1,g:-1,b:-1,hex:"none",error:1};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none"};!(_[f](b.toLowerCase().substring(0,2))||b.charAt()=="#")&&(b=bi(b));var c,d,e,g,h,i,j,k=b.match(N);if(k){if(k[2]){g=T(k[2].substring(5),16);e=T(k[2].substring(3,5),16);d=T(k[2].substring(1,3),16)}if(k[3]){g=T((i=k[3].charAt(3))+i,16);e=T((i=k[3].charAt(2))+i,16);d=T((i=k[3].charAt(1))+i,16)}if(k[4]){j=k[4][s]($);d=S(j[0]);j[0].slice(-1)=="%"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)=="%"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)=="%"&&(g*=2.55);k[1].toLowerCase().slice(0,4)=="rgba"&&(h=S(j[3]));j[3]&&j[3].slice(-1)=="%"&&(h/=100)}if(k[5]){j=k[5][s]($);d=S(j[0]);j[0].slice(-1)=="%"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)=="%"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)=="%"&&(g*=2.55);(j[0].slice(-3)=="deg"||j[0].slice(-1)=="┬░")&&(d/=360);k[1].toLowerCase().slice(0,4)=="hsba"&&(h=S(j[3]));j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,g,h)}if(k[6]){j=k[6][s]($);d=S(j[0]);j[0].slice(-1)=="%"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)=="%"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)=="%"&&(g*=2.55);(j[0].slice(-3)=="deg"||j[0].slice(-1)=="┬░")&&(d/=360);k[1].toLowerCase().slice(0,4)=="hsla"&&(h=S(j[3]));j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,g,h)}k={r:d,g:e,b:g};k.hex="#"+(16777216|g|e<<8|d<<16).toString(16).slice(1);a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1}},a);a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||0.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=0.075;if(b.h>1){b.h=0;b.s-=0.2;b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b})}return c.hex};a.getColor.reset=function(){delete this.start};a.parsePathString=bm(function(b){if(!b)return null;var c={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},d=[];a.is(b,G)&&a.is(b[0],G)&&(d=bo(b));d[w]||r(b)[Y](bb,function(a,b,e){var f=[],g=x.call(b);e[Y](bc,function(a,b){b&&f[L](+b)});if(g=="m"&&f[w]>2){d[L]([b][n](f.splice(0,2)));g="l";b=b=="m"?"l":"L"}while(f[w]>=c[g]){d[L]([b][n](f.splice(0,c[g])));if(!c[g])break}});d[H]=a._path2string;return d});a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=C(j,3)*a+C(j,2)*3*i*c+j*3*i*i*e+C(i,3)*g,l=C(j,3)*b+C(j,2)*3*i*d+j*3*i*i*f+C(i,3)*h,m=a+2*i*(c-a)+i*i*(e-2*c+a),n=b+2*i*(d-b)+i*i*(f-2*d+b),o=c+2*i*(e-c)+i*i*(g-2*e+c),p=d+2*i*(f-d)+i*i*(h-2*f+d),q=(1-i)*a+i*c,r=(1-i)*b+i*d,s=(1-i)*e+i*g,t=(1-i)*f+i*h,u=90-y.atan((m-o)/(n-p))*180/D;(m>o||n<p)&&(u+=180);return{x:k,y:l,m:{x:m,y:n},n:{x:o,y:p},start:{x:q,y:r},end:{x:s,y:t},alpha:u}};var bn=bm(function(a){if(!a)return{x:0,y:0,width:0,height:0};a=bw(a);var b=0,c=0,d=[],e=[],f;for(var g=0,h=a[w];g<h;g++){f=a[g];if(f[0]=="M"){b=f[1];c=f[2];d[L](b);e[L](c)}else{var i=bv(b,c,f[1],f[2],f[3],f[4],f[5],f[6]);d=d[n](i.min.x,i.max.x);e=e[n](i.min.y,i.max.y);b=f[5];c=f[6]}}var j=A[m](0,d),k=A[m](0,e);return{x:j,y:k,width:z[m](0,d)-j,height:z[m](0,e)-k}}),bo=function(b){var c=[];if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);for(var d=0,e=b[w];d<e;d++){c[d]=[];for(var f=0,g=b[d][w];f<g;f++)c[d][f]=b[d][f]}c[H]=a._path2string;return c},bp=bm(function(b){if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);var c=[],d=0,e=0,f=0,g=0,h=0;if(b[0][0]=="M"){d=b[0][1];e=b[0][2];f=d;g=e;h++;c[L](["M",d,e])}for(var i=h,j=b[w];i<j;i++){var k=c[i]=[],l=b[i];if(l[0]!=x.call(l[0])){k[0]=x.call(l[0]);switch(k[0]){case"a":k[1]=l[1];k[2]=l[2];k[3]=l[3];k[4]=l[4];k[5]=l[5];k[6]=+(l[6]-d).toFixed(3);k[7]=+(l[7]-e).toFixed(3);break;case"v":k[1]=+(l[1]-e).toFixed(3);break;case"m":f=l[1];g=l[2];default:for(var m=1,n=l[w];m<n;m++)k[m]=+(l[m]-(m%2?d:e)).toFixed(3)}}else{k=c[i]=[];if(l[0]=="m"){f=l[1]+d;g=l[2]+e}for(var o=0,p=l[w];o<p;o++)c[i][o]=l[o]}var q=c[i][w];switch(c[i][0]){case"z":d=f;e=g;break;case"h":d+=+c[i][q-1];break;case"v":e+=+c[i][q-1];break;default:d+=+c[i][q-2];e+=+c[i][q-1]}}c[H]=a._path2string;return c},0,bo),bq=bm(function(b){if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);var c=[],d=0,e=0,f=0,g=0,h=0;if(b[0][0]=="M"){d=+b[0][1];e=+b[0][2];f=d;g=e;h++;c[0]=["M",d,e]}for(var i=h,j=b[w];i<j;i++){var k=c[i]=[],l=b[i];if(l[0]!=V.call(l[0])){k[0]=V.call(l[0]);switch(k[0]){case"A":k[1]=l[1];k[2]=l[2];k[3]=l[3];k[4]=l[4];k[5]=l[5];k[6]=+(l[6]+d);k[7]=+(l[7]+e);break;case"V":k[1]=+l[1]+e;break;case"H":k[1]=+l[1]+d;break;case"M":f=+l[1]+d;g=+l[2]+e;default:for(var m=1,n=l[w];m<n;m++)k[m]=+l[m]+(m%2?d:e)}}else for(var o=0,p=l[w];o<p;o++)c[i][o]=l[o];switch(k[0]){case"Z":d=f;e=g;break;case"H":d=k[1];break;case"V":e=k[1];break;case"M":f=c[i][c[i][w]-2];g=c[i][c[i][w]-1];default:d=c[i][c[i][w]-2];e=c[i][c[i][w]-1]}}c[H]=a._path2string;return c},null,bo),br=function(a,b,c,d){return[a,b,c,d,c,d]},bs=function(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]},bt=function(a,b,c,d,e,f,g,h,i,j){var k=D*120/180,l=D/180*(+e||0),m=[],o,p=bm(function(a,b,c){var d=a*y.cos(c)-b*y.sin(c),e=a*y.sin(c)+b*y.cos(c);return{x:d,y:e}});if(j){G=j[0];H=j[1];E=j[2];F=j[3]}else{o=p(a,b,-l);a=o.x;b=o.y;o=p(h,i,-l);h=o.x;i=o.y;var q=y.cos(D/180*e),r=y.sin(D/180*e),t=(a-h)/2,u=(b-i)/2,x=t*t/(c*c)+u*u/(d*d);if(x>1){x=y.sqrt(x);c=x*c;d=x*d}var z=c*c,A=d*d,C=(f==g?-1:1)*y.sqrt(B((z*A-z*u*u-A*t*t)/(z*u*u+A*t*t))),E=C*c*u/d+(a+h)/2,F=C*-d*t/c+(b+i)/2,G=y.asin(((b-F)/d).toFixed(9)),H=y.asin(((i-F)/d).toFixed(9));G=a<E?D-G:G;H=h<E?D-H:H;G<0&&(G=D*2+G);H<0&&(H=D*2+H);g&&G>H&&(G=G-D*2);!g&&H>G&&(H=H-D*2)}var I=H-G;if(B(I)>k){var J=H,K=h,L=i;H=G+k*(g&&H>G?1:-1);h=E+c*y.cos(H);i=F+d*y.sin(H);m=bt(h,i,c,d,e,0,g,K,L,[H,J,E,F])}I=H-G;var M=y.cos(G),N=y.sin(G),O=y.cos(H),P=y.sin(H),Q=y.tan(I/4),R=4/3*c*Q,S=4/3*d*Q,T=[a,b],U=[a+R*N,b-S*M],V=[h+R*P,i-S*O],W=[h,i];U[0]=2*T[0]-U[0];U[1]=2*T[1]-U[1];{if(j)return[U,V,W][n](m);m=[U,V,W][n](m)[v]()[s](",");var X=[];for(var Y=0,Z=m[w];Y<Z;Y++)X[Y]=Y%2?p(m[Y-1],m[Y],l).y:p(m[Y],m[Y+1],l).x;return X}},bu=function(a,b,c,d,e,f,g,h,i){var j=1-i;return{x:C(j,3)*a+C(j,2)*3*i*c+j*3*i*i*e+C(i,3)*g,y:C(j,3)*b+C(j,2)*3*i*d+j*3*i*i*f+C(i,3)*h}},bv=bm(function(a,b,c,d,e,f,g,h){var i=e-2*c+a-(g-2*e+c),j=2*(c-a)-2*(e-c),k=a-c,l=(-j+y.sqrt(j*j-4*i*k))/2/i,n=(-j-y.sqrt(j*j-4*i*k))/2/i,o=[b,h],p=[a,g],q;B(l)>"1e12"&&(l=0.5);B(n)>"1e12"&&(n=0.5);if(l>0&&l<1){q=bu(a,b,c,d,e,f,g,h,l);p[L](q.x);o[L](q.y)}if(n>0&&n<1){q=bu(a,b,c,d,e,f,g,h,n);p[L](q.x);o[L](q.y)}i=f-2*d+b-(h-2*f+d);j=2*(d-b)-2*(f-d);k=b-d;l=(-j+y.sqrt(j*j-4*i*k))/2/i;n=(-j-y.sqrt(j*j-4*i*k))/2/i;B(l)>"1e12"&&(l=0.5);B(n)>"1e12"&&(n=0.5);if(l>0&&l<1){q=bu(a,b,c,d,e,f,g,h,l);p[L](q.x);o[L](q.y)}if(n>0&&n<1){q=bu(a,b,c,d,e,f,g,h,n);p[L](q.x);o[L](q.y)}return{min:{x:A[m](0,p),y:A[m](0,o)},max:{x:z[m](0,p),y:z[m](0,o)}}}),bw=bm(function(a,b){var c=bq(a),d=b&&bq(b),e={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1];b.Y=a[2];break;case"A":a=["C"][n](bt[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x));d=b.y+(b.y-(b.by||b.y));a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x));b.qy=b.y+(b.y-(b.qy||b.y));a=["C"][n](bs(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1];b.qy=a[2];a=["C"][n](bs(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](br(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](br(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](br(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](br(b.x,b.y,b.X,b.Y));break}return a},h=function(a,b){if(a[b][w]>7){a[b].shift();var e=a[b];while(e[w])a.splice(b++,0,["C"][n](e.splice(0,6)));a.splice(b,1);k=z(c[w],d&&d[w]||0)}},i=function(a,b,e,f,g){if(a&&b&&a[g][0]=="M"&&b[g][0]!="M"){b.splice(g,0,["M",f.x,f.y]);e.bx=0;e.by=0;e.x=a[g][1];e.y=a[g][2];k=z(c[w],d&&d[w]||0)}};for(var j=0,k=z(c[w],d&&d[w]||0);j<k;j++){c[j]=g(c[j],e);h(c,j);d&&(d[j]=g(d[j],f));d&&h(d,j);i(c,d,e,f,j);i(d,c,f,e,j);var l=c[j],o=d&&d[j],p=l[w],q=d&&o[w];e.x=l[p-2];e.y=l[p-1];e.bx=S(l[p-4])||e.x;e.by=S(l[p-3])||e.y;f.bx=d&&(S(o[q-4])||f.x);f.by=d&&(S(o[q-3])||f.y);f.x=d&&o[q-2];f.y=d&&o[q-1]}return d?[c,d]:c},null,bo),bx=bm(function(b){var c=[];for(var d=0,e=b[w];d<e;d++){var f={},g=b[d].match(/^([^:]*):?([\d\.]*)/);f.color=a.getRGB(g[1]);if(f.color.error)return null;f.color=f.color.hex;g[2]&&(f.offset=g[2]+"%");c[L](f)}for(d=1,e=c[w]-1;d<e;d++){if(!c[d].offset){var h=S(c[d-1].offset||0),i=0;for(var j=d+1;j<e;j++){if(c[j].offset){i=c[j].offset;break}}if(!i){i=100;j=e}i=S(i);var k=(i-h)/(j-d+1);for(;d<j;d++){h+=k;c[d].offset=h+"%"}}}return c}),by=function(b,c,d,e){var f;if(a.is(b,F)||a.is(b,"object")){f=a.is(b,F)?g.getElementById(b):b;if(f.tagName)return c==null?{container:f,width:f.style.pixelWidth||f.offsetWidth,height:f.style.pixelHeight||f.offsetHeight}:{container:f,width:c,height:d}}else return{container:1,x:b,y:c,width:d,height:e}},bz=function(a,b){var c=this;for(var d in b){if(b[f](d)&&!(d in a))switch(typeof b[d]){case"function":(function(b){a[d]=a===c?b:function(){return b[m](c,arguments)}})(b[d]);break;case"object":a[d]=a[d]||{};bz.call(this,a[d],b[d]);break;default:a[d]=b[d];break}}},bA=function(a,b){a==b.top&&(b.top=a.prev);a==b.bottom&&(b.bottom=a.next);a.next&&(a.next.prev=a.prev);a.prev&&(a.prev.next=a.next)},bB=function(a,b){if(b.top===a)return;bA(a,b);a.next=null;a.prev=b.top;b.top.next=a;b.top=a},bC=function(a,b){if(b.bottom===a)return;bA(a,b);a.next=b.bottom;a.prev=null;b.bottom.prev=a;b.bottom=a},bD=function(a,b,c){bA(a,c);b==c.top&&(c.top=a);b.next&&(b.next.prev=a);a.next=b.next;a.prev=b;b.next=a},bE=function(a,b,c){bA(a,c);b==c.bottom&&(c.bottom=a);b.prev&&(b.prev.next=a);a.prev=b.prev;b.prev=a;a.next=b},bF=function(a){return function(){throw new Error("Rapha├лl: you are calling to method тАЬ"+a+"тАЭ of removed object")}};a.pathToRelative=bp;if(a.svg){k.svgns="http://www.w3.org/2000/svg";k.xlink="http://www.w3.org/1999/xlink";Q=function(a){return+a+(~(~a)===a)*0.5};var bG=function(a,b){if(b)for(var c in b)b[f](c)&&a[R](c,r(b[c]));else{a=g.createElementNS(k.svgns,a);a.style.webkitTapHighlightColor="rgba(0,0,0,0)";return a}};a[H]=function(){return"Your browser supports SVG.\nYou are running Rapha├лl "+this.version};var bH=function(a,b){var c=bG("path");b.canvas&&b.canvas[l](c);var d=new bN(c,b);d.type="path";bK(d,{fill:"none",stroke:"#000",path:a});return d},bI=function(a,b,c){var d="linear",e=0.5,f=0.5,h=a.style;b=r(b)[Y](bd,function(a,b,c){d="radial";if(b&&c){e=S(b);f=S(c);var g=(f>0.5)*2-1;C(e-0.5,2)+C(f-0.5,2)>0.25&&(f=y.sqrt(0.25-C(e-0.5,2))*g+0.5)&&f!=0.5&&(f=f.toFixed(5)-0.00001*g)}return p});b=b[s](/\s*\-\s*/);if(d=="linear"){var i=b.shift();i=-S(i);if(isNaN(i))return null;var j=[0,0,y.cos(i*D/180),y.sin(i*D/180)],k=1/(z(B(j[2]),B(j[3]))||1);j[2]*=k;j[3]*=k;if(j[2]<0){j[0]=-j[2];j[2]=0}if(j[3]<0){j[1]=-j[3];j[3]=0}}var m=bx(b);if(!m)return null;var n=a.getAttribute(I);n=n.match(/^url\(#(.*)\)$/);n&&c.defs.removeChild(g.getElementById(n[1]));var o=bG(d+"Gradient");o.id=bh();bG(o,d=="radial"?{fx:e,fy:f}:{x1:j[0],y1:j[1],x2:j[2],y2:j[3]});c.defs[l](o);for(var q=0,t=m[w];q<t;q++){var u=bG("stop");bG(u,{offset:m[q].offset?m[q].offset:q?"100%":"0%","stop-color":m[q].color||"#fff"});o[l](u)}bG(a,{fill:"url(#"+o.id+")",opacity:1,"fill-opacity":1});h.fill=p;h.opacity=1;h.fillOpacity=1;return 1},bJ=function(b){var c=b.getBBox();bG(b.pattern,{patternTransform:a.format("translate({0},{1})",c.x,c.y)})},bK=function(c,d){var e={"":[0],none:[0],"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},h=c.node,i=c.attrs,j=c.rotate(),k=function(a,b){b=e[x.call(b)];if(b){var c=a.attrs["stroke-width"]||"1",f=({round:c,square:c,butt:0})[a.attrs["stroke-linecap"]||d["stroke-linecap"]]||0,g=[],i=b[w];while(i--)g[i]=b[i]*c+(i%2?1:-1)*f;bG(h,{"stroke-dasharray":g[v](",")})}};d[f]("rotation")&&(j=d.rotation);var m=r(j)[s](b);if(m.length-1){m[1]=+m[1];m[2]=+m[2]}else m=null;S(j)&&c.rotate(0,true);for(var n in d){if(d[f](n)){if(!W[f](n))continue;var o=d[n];i[n]=o;switch(n){case"blur":c.blur(o);break;case"rotation":c.rotate(o,true);break;case"href":case"title":case"target":var t=h.parentNode;if(x.call(t.tagName)!="a"){var u=bG("a");t.insertBefore(u,h);u[l](h);t=u}n=="target"&&o=="blank"?t.setAttributeNS(c.paper.xlink,"show","new"):t.setAttributeNS(c.paper.xlink,n,o);break;case"cursor":h.style.cursor=o;break;case"clip-rect":var y=r(o)[s](b);if(y[w]==4){c.clip&&c.clip.parentNode.parentNode.removeChild(c.clip.parentNode);var z=bG("clipPath"),A=bG("rect");z.id=bh();bG(A,{x:y[0],y:y[1],width:y[2],height:y[3]});z[l](A);c.paper.defs[l](z);bG(h,{"clip-path":"url(#"+z.id+")"});c.clip=A}if(!o){var B=g.getElementById(h.getAttribute("clip-path")[Y](/(^url\(#|\)$)/g,p));B&&B.parentNode.removeChild(B);bG(h,{"clip-path":p});delete c.clip}break;case"path":c.type=="path"&&bG(h,{d:o?i.path=bq(o):"M0,0"});break;case"width":h[R](n,o);if(i.fx){n="x";o=i.x}else break;case"x":i.fx&&(o=-i.x-(i.width||0));case"rx":if(n=="rx"&&c.type=="rect")break;case"cx":m&&(n=="x"||n=="cx")&&(m[1]+=o-i[n]);h[R](n,o);c.pattern&&bJ(c);break;case"height":h[R](n,o);if(i.fy){n="y";o=i.y}else break;case"y":i.fy&&(o=-i.y-(i.height||0));case"ry":if(n=="ry"&&c.type=="rect")break;case"cy":m&&(n=="y"||n=="cy")&&(m[2]+=o-i[n]);h[R](n,o);c.pattern&&bJ(c);break;case"r":c.type=="rect"?bG(h,{rx:o,ry:o}):h[R](n,o);break;case"src":c.type=="image"&&h.setAttributeNS(c.paper.xlink,"href",o);break;case"stroke-width":h.style.strokeWidth=o;h[R](n,o);i["stroke-dasharray"]&&k(c,i["stroke-dasharray"]);break;case"stroke-dasharray":k(c,o);break;case"translation":var C=r(o)[s](b);C[0]=+C[0]||0;C[1]=+C[1]||0;if(m){m[1]+=C[0];m[2]+=C[1]}cz.call(c,C[0],C[1]);break;case"scale":C=r(o)[s](b);c.scale(+C[0]||1,+C[1]||+C[0]||1,isNaN(S(C[2]))?null:+C[2],isNaN(S(C[3]))?null:+C[3]);break;case I:var D=r(o).match(M);if(D){z=bG("pattern");var E=bG("image");z.id=bh();bG(z,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1});bG(E,{x:0,y:0});E.setAttributeNS(c.paper.xlink,"href",D[1]);z[l](E);var F=g.createElement("img");F.style.cssText="position:absolute;left:-9999em;top-9999em";F.onload=function(){bG(z,{width:this.offsetWidth,height:this.offsetHeight});bG(E,{width:this.offsetWidth,height:this.offsetHeight});g.body.removeChild(this);c.paper.safari()};g.body[l](F);F.src=D[1];c.paper.defs[l](z);h.style.fill="url(#"+z.id+")";bG(h,{fill:"url(#"+z.id+")"});c.pattern=z;c.pattern&&bJ(c);break}var G=a.getRGB(o);if(G.error)if((({circle:1,ellipse:1})[f](c.type)||r(o).charAt()!="r")&&bI(h,o,c.paper)){i.gradient=o;i.fill="none";break}else{delete d.gradient;delete i.gradient;!a.is(i.opacity,"undefined")&&a.is(d.opacity,"undefined")&&bG(h,{opacity:i.opacity});!a.is(i["fill-opacity"],"undefined")&&a.is(d["fill-opacity"],"undefined")&&bG(h,{"fill-opacity":i["fill-opacity"]})}G[f]("opacity")&&bG(h,{"fill-opacity":G.opacity>1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(o);h[R](n,G.hex);n=="stroke"&&G[f]("opacity")&&bG(h,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity});break;case"gradient":(({circle:1,ellipse:1})[f](c.type)||r(o).charAt()!="r")&&bI(h,o,c.paper);break;case"opacity":i.gradient&&!i[f]("stroke-opacity")&&bG(h,{"stroke-opacity":o>1?o/100:o});case"fill-opacity":if(i.gradient){var H=g.getElementById(h.getAttribute(I)[Y](/^url\(#|\)$/g,p));if(H){var J=H.getElementsByTagName("stop");J[J[w]-1][R]("stop-opacity",o)}break}default:n=="font-size"&&(o=T(o,10)+"px");var K=n[Y](/(\-.)/g,function(a){return V.call(a.substring(1))});h.style[K]=o;h[R](n,o);break}}}bM(c,d);m?c.rotate(m.join(q)):S(j)&&c.rotate(j,true)},bL=1.2,bM=function(b,c){if(b.type!="text"||!(c[f]("text")||c[f]("font")||c[f]("font-size")||c[f]("x")||c[f]("y")))return;var d=b.attrs,e=b.node,h=e.firstChild?T(g.defaultView.getComputedStyle(e.firstChild,p).getPropertyValue("font-size"),10):10;if(c[f]("text")){d.text=c.text;while(e.firstChild)e.removeChild(e.firstChild);var i=r(c.text)[s]("\n");for(var j=0,k=i[w];j<k;j++)if(i[j]){var m=bG("tspan");j&&bG(m,{dy:h*bL,x:d.x});m[l](g.createTextNode(i[j]));e[l](m)}}else{i=e.getElementsByTagName("tspan");for(j=0,k=i[w];j<k;j++)j&&bG(i[j],{dy:h*bL,x:d.x})}bG(e,{y:d.y});var n=b.getBBox(),o=d.y-(n.y+n.height/2);o&&a.is(o,"finite")&&bG(e,{y:d.y+o})},bN=function(b,c){var d=0,e=0;this[0]=b;this.id=a._oid++;this.node=b;b.raphael=this;this.paper=c;this.attrs=this.attrs||{};this.transformations=[];this._={tx:0,ty:0,rt:{deg:0,cx:0,cy:0},sx:1,sy:1};!c.bottom&&(c.bottom=this);this.prev=c.top;c.top&&(c.top.next=this);c.top=this;this.next=null},bO=bN[e];bN[e].rotate=function(c,d,e){if(this.removed)return this;if(c==null){if(this._.rt.cx)return[this._.rt.deg,this._.rt.cx,this._.rt.cy][v](q);return this._.rt.deg}var f=this.getBBox();c=r(c)[s](b);if(c[w]-1){d=S(c[1]);e=S(c[2])}c=S(c[0]);d!=null&&d!==false?this._.rt.deg=c:this._.rt.deg+=c;e==null&&(d=null);this._.rt.cx=d;this._.rt.cy=e;d=d==null?f.x+f.width/2:d;e=e==null?f.y+f.height/2:e;if(this._.rt.deg){this.transformations[0]=a.format("rotate({0} {1} {2})",this._.rt.deg,d,e);this.clip&&bG(this.clip,{transform:a.format("rotate({0} {1} {2})",-this._.rt.deg,d,e)})}else{this.transformations[0]=p;this.clip&&bG(this.clip,{transform:p})}bG(this.node,{transform:this.transformations[v](q)});return this};bN[e].hide=function(){!this.removed&&(this.node.style.display="none");return this};bN[e].show=function(){!this.removed&&(this.node.style.display="");return this};bN[e].remove=function(){if(this.removed)return;bA(this,this.paper);this.node.parentNode.removeChild(this.node);for(var a in this)delete this[a];this.removed=true};bN[e].getBBox=function(){if(this.removed)return this;if(this.type=="path")return bn(this.attrs.path);if(this.node.style.display=="none"){this.show();var a=true}var b={};try{b=this.node.getBBox()}catch(a){}finally{b=b||{}}if(this.type=="text"){b={x:b.x,y:Infinity,width:0,height:0};for(var c=0,d=this.node.getNumberOfChars();c<d;c++){var e=this.node.getExtentOfChar(c);e.y<b.y&&(b.y=e.y);e.y+e.height-b.y>b.height&&(b.height=e.y+e.height-b.y);e.x+e.width-b.x>b.width&&(b.width=e.x+e.width-b.x)}}a&&this.hide();return b};bN[e].attr=function(b,c){if(this.removed)return this;if(b==null){var d={};for(var e in this.attrs)this.attrs[f](e)&&(d[e]=this.attrs[e]);this._.rt.deg&&(d.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(d.scale=this.scale());d.gradient&&d.fill=="none"&&(d.fill=d.gradient)&&delete d.gradient;return d}if(c==null&&a.is(b,F)){if(b=="translation")return cz.call(this);if(b=="rotation")return this.rotate();if(b=="scale")return this.scale();if(b==I&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[b]}if(c==null&&a.is(b,G)){var g={};for(var h=0,i=b.length;h<i;h++)g[b[h]]=this.attr(b[h]);return g}if(c!=null){var j={};j[b]=c}else b!=null&&a.is(b,"object")&&(j=b);for(var k in this.paper.customAttributes)if(this.paper.customAttributes[f](k)&&j[f](k)&&a.is(this.paper.customAttributes[k],"function")){var l=this.paper.customAttributes[k].apply(this,[][n](j[k]));this.attrs[k]=j[k];for(var m in l)l[f](m)&&(j[m]=l[m])}bK(this,j);return this};bN[e].toFront=function(){if(this.removed)return this;this.node.parentNode[l](this.node);var a=this.paper;a.top!=this&&bB(this,a);return this};bN[e].toBack=function(){if(this.removed)return this;if(this.node.parentNode.firstChild!=this.node){this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild);bC(this,this.paper);var a=this.paper}return this};bN[e].insertAfter=function(a){if(this.removed)return this;var b=a.node||a[a.length-1].node;b.nextSibling?b.parentNode.insertBefore(this.node,b.nextSibling):b.parentNode[l](this.node);bD(this,a,this.paper);return this};bN[e].insertBefore=function(a){if(this.removed)return this;var b=a.node||a[0].node;b.parentNode.insertBefore(this.node,b);bE(this,a,this.paper);return this};bN[e].blur=function(a){var b=this;if(+a!==0){var c=bG("filter"),d=bG("feGaussianBlur");b.attrs.blur=a;c.id=bh();bG(d,{stdDeviation:+a||1.5});c.appendChild(d);b.paper.defs.appendChild(c);b._blur=c;bG(b.node,{filter:"url(#"+c.id+")"})}else{if(b._blur){b._blur.parentNode.removeChild(b._blur);delete b._blur;delete b.attrs.blur}b.node.removeAttribute("filter")}};var bP=function(a,b,c,d){var e=bG("circle");a.canvas&&a.canvas[l](e);var f=new bN(e,a);f.attrs={cx:b,cy:c,r:d,fill:"none",stroke:"#000"};f.type="circle";bG(e,f.attrs);return f},bQ=function(a,b,c,d,e,f){var g=bG("rect");a.canvas&&a.canvas[l](g);var h=new bN(g,a);h.attrs={x:b,y:c,width:d,height:e,r:f||0,rx:f||0,ry:f||0,fill:"none",stroke:"#000"};h.type="rect";bG(g,h.attrs);return h},bR=function(a,b,c,d,e){var f=bG("ellipse");a.canvas&&a.canvas[l](f);var g=new bN(f,a);g.attrs={cx:b,cy:c,rx:d,ry:e,fill:"none",stroke:"#000"};g.type="ellipse";bG(f,g.attrs);return g},bS=function(a,b,c,d,e,f){var g=bG("image");bG(g,{x:c,y:d,width:e,height:f,preserveAspectRatio:"none"});g.setAttributeNS(a.xlink,"href",b);a.canvas&&a.canvas[l](g);var h=new bN(g,a);h.attrs={x:c,y:d,width:e,height:f,src:b};h.type="image";return h},bT=function(a,b,c,d){var e=bG("text");bG(e,{x:b,y:c,"text-anchor":"middle"});a.canvas&&a.canvas[l](e);var f=new bN(e,a);f.attrs={x:b,y:c,"text-anchor":"middle",text:d,font:W.font,stroke:"none",fill:"#000"};f.type="text";bK(f,f.attrs);return f},bU=function(a,b){this.width=a||this.width;this.height=b||this.height;this.canvas[R]("width",this.width);this.canvas[R]("height",this.height);return this},bV=function(){var b=by[m](0,arguments),c=b&&b.container,d=b.x,e=b.y,f=b.width,h=b.height;if(!c)throw new Error("SVG container not found.");var i=bG("svg");d=d||0;e=e||0;f=f||512;h=h||342;bG(i,{xmlns:"http://www.w3.org/2000/svg",version:1.1,width:f,height:h});if(c==1){i.style.cssText="position:absolute;left:"+d+"px;top:"+e+"px";g.body[l](i)}else c.firstChild?c.insertBefore(i,c.firstChild):c[l](i);c=new j;c.width=f;c.height=h;c.canvas=i;bz.call(c,c,a.fn);c.clear();return c};k.clear=function(){var a=this.canvas;while(a.firstChild)a.removeChild(a.firstChild);this.bottom=this.top=null;(this.desc=bG("desc"))[l](g.createTextNode("Created with Rapha├лl"));a[l](this.desc);a[l](this.defs=bG("defs"))};k.remove=function(){this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=bF(a)}}if(a.vml){var bW={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},bX=/([clmz]),?([^clmz]*)/gi,bY=/ progid:\S+Blur\([^\)]+\)/g,bZ=/-?[^,\s-]+/g,b$=1000+q+1000,b_=10,ca={path:1,rect:1},cb=function(a){var b=/[ahqstv]/ig,c=bq;r(a).match(b)&&(c=bw);b=/[clmz]/g;if(c==bq&&!r(a).match(b)){var d=r(a)[Y](bX,function(a,b,c){var d=[],e=x.call(b)=="m",f=bW[b];c[Y](bZ,function(a){if(e&&d[w]==2){f+=d+bW[b=="m"?"l":"L"];d=[]}d[L](Q(a*b_))});return f+d});return d}var e=c(a),f,g;d=[];for(var h=0,i=e[w];h<i;h++){f=e[h];g=x.call(e[h][0]);g=="z"&&(g="x");for(var j=1,k=f[w];j<k;j++)g+=Q(f[j]*b_)+(j!=k-1?",":p);d[L](g)}return d[v](q)};a[H]=function(){return"Your browser doesnтАЩt support SVG. Falling down to VML.\nYou are running Rapha├лl "+this.version};bH=function(a,b){var c=cd("group");c.style.cssText="position:absolute;left:0;top:0;width:"+b.width+"px;height:"+b.height+"px";c.coordsize=b.coordsize;c.coordorigin=b.coordorigin;var d=cd("shape"),e=d.style;e.width=b.width+"px";e.height=b.height+"px";d.coordsize=b$;d.coordorigin=b.coordorigin;c[l](d);var f=new bN(d,c,b),g={fill:"none",stroke:"#000"};a&&(g.path=a);f.type="path";f.path=[];f.Path=p;bK(f,g);b.canvas[l](c);return f};bK=function(c,d){c.attrs=c.attrs||{};var e=c.node,h=c.attrs,i=e.style,j,k=(d.x!=h.x||d.y!=h.y||d.width!=h.width||d.height!=h.height||d.r!=h.r)&&c.type=="rect",m=c;for(var n in d)d[f](n)&&(h[n]=d[n]);if(k){h.path=cc(h.x,h.y,h.width,h.height,h.r);c.X=h.x;c.Y=h.y;c.W=h.width;c.H=h.height}d.href&&(e.href=d.href);d.title&&(e.title=d.title);d.target&&(e.target=d.target);d.cursor&&(i.cursor=d.cursor);"blur"in d&&c.blur(d.blur);if(d.path&&c.type=="path"||k)e.path=cb(h.path);d.rotation!=null&&c.rotate(d.rotation,true);if(d.translation){j=r(d.translation)[s](b);cz.call(c,j[0],j[1]);if(c._.rt.cx!=null){c._.rt.cx+=+j[0];c._.rt.cy+=+j[1];c.setBox(c.attrs,j[0],j[1])}}if(d.scale){j=r(d.scale)[s](b);c.scale(+j[0]||1,+j[1]||+j[0]||1,+j[2]||null,+j[3]||null)}if("clip-rect"in d){var o=r(d["clip-rect"])[s](b);if(o[w]==4){o[2]=+o[2]+ +o[0];o[3]=+o[3]+ +o[1];var q=e.clipRect||g.createElement("div"),t=q.style,u=e.parentNode;t.clip=a.format("rect({1}px {2}px {3}px {0}px)",o);if(!e.clipRect){t.position="absolute";t.top=0;t.left=0;t.width=c.paper.width+"px";t.height=c.paper.height+"px";u.parentNode.insertBefore(q,u);q[l](u);e.clipRect=q}}d["clip-rect"]||e.clipRect&&(e.clipRect.style.clip=p)}c.type=="image"&&d.src&&(e.src=d.src);if(c.type=="image"&&d.opacity){e.filterOpacity=U+".Alpha(opacity="+d.opacity*100+")";i.filter=(e.filterMatrix||p)+(e.filterOpacity||p)}d.font&&(i.font=d.font);d["font-family"]&&(i.fontFamily="\""+d["font-family"][s](",")[0][Y](/^['"]+|['"]+$/g,p)+"\"");d["font-size"]&&(i.fontSize=d["font-size"]);d["font-weight"]&&(i.fontWeight=d["font-weight"]);d["font-style"]&&(i.fontStyle=d["font-style"]);if(d.opacity!=null||d["stroke-width"]!=null||d.fill!=null||d.stroke!=null||d["stroke-width"]!=null||d["stroke-opacity"]!=null||d["fill-opacity"]!=null||d["stroke-dasharray"]!=null||d["stroke-miterlimit"]!=null||d["stroke-linejoin"]!=null||d["stroke-linecap"]!=null){e=c.shape||e;var v=e.getElementsByTagName(I)&&e.getElementsByTagName(I)[0],x=false;!v&&(x=v=cd(I));if("fill-opacity"in d||"opacity"in d){var y=((+h["fill-opacity"]+1||2)-1)*((+h.opacity+1||2)-1)*((+a.getRGB(d.fill).o+1||2)-1);y=A(z(y,0),1);v.opacity=y}d.fill&&(v.on=true);if(v.on==null||d.fill=="none")v.on=false;if(v.on&&d.fill){var B=d.fill.match(M);if(B){v.src=B[1];v.type="tile"}else{v.color=a.getRGB(d.fill).hex;v.src=p;v.type="solid";if(a.getRGB(d.fill).error&&(m.type in{circle:1,ellipse:1}||r(d.fill).charAt()!="r")&&bI(m,d.fill)){h.fill="none";h.gradient=d.fill}}}x&&e[l](v);var C=e.getElementsByTagName("stroke")&&e.getElementsByTagName("stroke")[0],D=false;!C&&(D=C=cd("stroke"));if(d.stroke&&d.stroke!="none"||d["stroke-width"]||d["stroke-opacity"]!=null||d["stroke-dasharray"]||d["stroke-miterlimit"]||d["stroke-linejoin"]||d["stroke-linecap"])C.on=true;(d.stroke=="none"||C.on==null||d.stroke==0||d["stroke-width"]==0)&&(C.on=false);var E=a.getRGB(d.stroke);C.on&&d.stroke&&(C.color=E.hex);y=((+h["stroke-opacity"]+1||2)-1)*((+h.opacity+1||2)-1)*((+E.o+1||2)-1);var F=(S(d["stroke-width"])||1)*0.75;y=A(z(y,0),1);d["stroke-width"]==null&&(F=h["stroke-width"]);d["stroke-width"]&&(C.weight=F);F&&F<1&&(y*=F)&&(C.weight=1);C.opacity=y;d["stroke-linejoin"]&&(C.joinstyle=d["stroke-linejoin"]||"miter");C.miterlimit=d["stroke-miterlimit"]||8;d["stroke-linecap"]&&(C.endcap=d["stroke-linecap"]=="butt"?"flat":d["stroke-linecap"]=="square"?"square":"round");if(d["stroke-dasharray"]){var G={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};C.dashstyle=G[f](d["stroke-dasharray"])?G[d["stroke-dasharray"]]:p}D&&e[l](C)}if(m.type=="text"){i=m.paper.span.style;h.font&&(i.font=h.font);h["font-family"]&&(i.fontFamily=h["font-family"]);h["font-size"]&&(i.fontSize=h["font-size"]);h["font-weight"]&&(i.fontWeight=h["font-weight"]);h["font-style"]&&(i.fontStyle=h["font-style"]);m.node.string&&(m.paper.span.innerHTML=r(m.node.string)[Y](/</g,"&#60;")[Y](/&/g,"&#38;")[Y](/\n/g,"<br>"));m.W=h.w=m.paper.span.offsetWidth;m.H=h.h=m.paper.span.offsetHeight;m.X=h.x;m.Y=h.y+Q(m.H/2);switch(h["text-anchor"]){case"start":m.node.style["v-text-align"]="left";m.bbx=Q(m.W/2);break;case"end":m.node.style["v-text-align"]="right";m.bbx=-Q(m.W/2);break;default:m.node.style["v-text-align"]="center";break}}};bI=function(a,b){a.attrs=a.attrs||{};var c=a.attrs,d,e="linear",f=".5 .5";a.attrs.gradient=b;b=r(b)[Y](bd,function(a,b,c){e="radial";if(b&&c){b=S(b);c=S(c);C(b-0.5,2)+C(c-0.5,2)>0.25&&(c=y.sqrt(0.25-C(b-0.5,2))*((c>0.5)*2-1)+0.5);f=b+q+c}return p});b=b[s](/\s*\-\s*/);if(e=="linear"){var g=b.shift();g=-S(g);if(isNaN(g))return null}var h=bx(b);if(!h)return null;a=a.shape||a.node;d=a.getElementsByTagName(I)[0]||cd(I);!d.parentNode&&a.appendChild(d);if(h[w]){d.on=true;d.method="none";d.color=h[0].color;d.color2=h[h[w]-1].color;var i=[];for(var j=0,k=h[w];j<k;j++)h[j].offset&&i[L](h[j].offset+q+h[j].color);d.colors&&(d.colors.value=i[w]?i[v]():"0% "+d.color);if(e=="radial"){d.type="gradientradial";d.focus="100%";d.focussize=f;d.focusposition=f}else{d.type="gradient";d.angle=(270-g)%360}}return 1};bN=function(b,c,d){var e=0,f=0,g=0,h=1;this[0]=b;this.id=a._oid++;this.node=b;b.raphael=this;this.X=0;this.Y=0;this.attrs={};this.Group=c;this.paper=d;this._={tx:0,ty:0,rt:{deg:0},sx:1,sy:1};!d.bottom&&(d.bottom=this);this.prev=d.top;d.top&&(d.top.next=this);d.top=this;this.next=null};bO=bN[e];bO.rotate=function(a,c,d){if(this.removed)return this;if(a==null){if(this._.rt.cx)return[this._.rt.deg,this._.rt.cx,this._.rt.cy][v](q);return this._.rt.deg}a=r(a)[s](b);if(a[w]-1){c=S(a[1]);d=S(a[2])}a=S(a[0]);c!=null?this._.rt.deg=a:this._.rt.deg+=a;d==null&&(c=null);this._.rt.cx=c;this._.rt.cy=d;this.setBox(this.attrs,c,d);this.Group.style.rotation=this._.rt.deg;return this};bO.setBox=function(a,b,c){if(this.removed)return this;var d=this.Group.style,e=this.shape&&this.shape.style||this.node.style;a=a||{};for(var g in a)a[f](g)&&(this.attrs[g]=a[g]);b=b||this._.rt.cx;c=c||this._.rt.cy;var h=this.attrs,i,j,k,l;switch(this.type){case"circle":i=h.cx-h.r;j=h.cy-h.r;k=l=h.r*2;break;case"ellipse":i=h.cx-h.rx;j=h.cy-h.ry;k=h.rx*2;l=h.ry*2;break;case"image":i=+h.x;j=+h.y;k=h.width||0;l=h.height||0;break;case"text":this.textpath.v=["m",Q(h.x),", ",Q(h.y-2),"l",Q(h.x)+1,", ",Q(h.y-2)][v](p);i=h.x-Q(this.W/2);j=h.y-this.H/2;k=this.W;l=this.H;break;case"rect":case"path":if(this.attrs.path){var m=bn(this.attrs.path);i=m.x;j=m.y;k=m.width;l=m.height}else{i=0;j=0;k=this.paper.width;l=this.paper.height}break;default:i=0;j=0;k=this.paper.width;l=this.paper.height;break}b=b==null?i+k/2:b;c=c==null?j+l/2:c;var n=b-this.paper.width/2,o=c-this.paper.height/2,q;d.left!=(q=n+"px")&&(d.left=q);d.top!=(q=o+"px")&&(d.top=q);this.X=ca[f](this.type)?-n:i;this.Y=ca[f](this.type)?-o:j;this.W=k;this.H=l;if(ca[f](this.type)){e.left!=(q=-n*b_+"px")&&(e.left=q);e.top!=(q=-o*b_+"px")&&(e.top=q)}else if(this.type=="text"){e.left!=(q=-n+"px")&&(e.left=q);e.top!=(q=-o+"px")&&(e.top=q)}else{d.width!=(q=this.paper.width+"px")&&(d.width=q);d.height!=(q=this.paper.height+"px")&&(d.height=q);e.left!=(q=i-n+"px")&&(e.left=q);e.top!=(q=j-o+"px")&&(e.top=q);e.width!=(q=k+"px")&&(e.width=q);e.height!=(q=l+"px")&&(e.height=q)}};bO.hide=function(){!this.removed&&(this.Group.style.display="none");return this};bO.show=function(){!this.removed&&(this.Group.style.display="block");return this};bO.getBBox=function(){if(this.removed)return this;if(ca[f](this.type))return bn(this.attrs.path);return{x:this.X+(this.bbx||0),y:this.Y,width:this.W,height:this.H}};bO.remove=function(){if(this.removed)return;bA(this,this.paper);this.node.parentNode.removeChild(this.node);this.Group.parentNode.removeChild(this.Group);this.shape&&this.shape.parentNode.removeChild(this.shape);for(var a in this)delete this[a];this.removed=true};bO.attr=function(b,c){if(this.removed)return this;if(b==null){var d={};for(var e in this.attrs)this.attrs[f](e)&&(d[e]=this.attrs[e]);this._.rt.deg&&(d.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(d.scale=this.scale());d.gradient&&d.fill=="none"&&(d.fill=d.gradient)&&delete d.gradient;return d}if(c==null&&a.is(b,"string")){if(b=="translation")return cz.call(this);if(b=="rotation")return this.rotate();if(b=="scale")return this.scale();if(b==I&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[b]}if(this.attrs&&c==null&&a.is(b,G)){var g,h={};for(e=0,g=b[w];e<g;e++)h[b[e]]=this.attr(b[e]);return h}var i;if(c!=null){i={};i[b]=c}c==null&&a.is(b,"object")&&(i=b);if(i){for(var j in this.paper.customAttributes)if(this.paper.customAttributes[f](j)&&i[f](j)&&a.is(this.paper.customAttributes[j],"function")){var k=this.paper.customAttributes[j].apply(this,[][n](i[j]));this.attrs[j]=i[j];for(var l in k)k[f](l)&&(i[l]=k[l])}i.text&&this.type=="text"&&(this.node.string=i.text);bK(this,i);i.gradient&&(({circle:1,ellipse:1})[f](this.type)||r(i.gradient).charAt()!="r")&&bI(this,i.gradient);(!ca[f](this.type)||this._.rt.deg)&&this.setBox(this.attrs)}return this};bO.toFront=function(){!this.removed&&this.Group.parentNode[l](this.Group);this.paper.top!=this&&bB(this,this.paper);return this};bO.toBack=function(){if(this.removed)return this;if(this.Group.parentNode.firstChild!=this.Group){this.Group.parentNode.insertBefore(this.Group,this.Group.parentNode.firstChild);bC(this,this.paper)}return this};bO.insertAfter=function(a){if(this.removed)return this;a.constructor==cC&&(a=a[a.length-1]);a.Group.nextSibling?a.Group.parentNode.insertBefore(this.Group,a.Group.nextSibling):a.Group.parentNode[l](this.Group);bD(this,a,this.paper);return this};bO.insertBefore=function(a){if(this.removed)return this;a.constructor==cC&&(a=a[0]);a.Group.parentNode.insertBefore(this.Group,a.Group);bE(this,a,this.paper);return this};bO.blur=function(b){var c=this.node.runtimeStyle,d=c.filter;d=d.replace(bY,p);if(+b!==0){this.attrs.blur=b;c.filter=d+q+U+".Blur(pixelradius="+(+b||1.5)+")";c.margin=a.format("-{0}px 0 0 -{0}px",Q(+b||1.5))}else{c.filter=d;c.margin=0;delete this.attrs.blur}};bP=function(a,b,c,d){var e=cd("group"),f=cd("oval"),g=f.style;e.style.cssText="position:absolute;left:0;top:0;width:"+a.width+"px;height:"+a.height+"px";e.coordsize=b$;e.coordorigin=a.coordorigin;e[l](f);var h=new bN(f,e,a);h.type="circle";bK(h,{stroke:"#000",fill:"none"});h.attrs.cx=b;h.attrs.cy=c;h.attrs.r=d;h.setBox({x:b-d,y:c-d,width:d*2,height:d*2});a.canvas[l](e);return h};function cc(b,c,d,e,f){return f?a.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z",b+f,c,d-f*2,f,-f,e-f*2,f*2-d,f*2-e):a.format("M{0},{1}l{2},0,0,{3},{4},0z",b,c,d,e,-d)}bQ=function(a,b,c,d,e,f){var g=cc(b,c,d,e,f),h=a.path(g),i=h.attrs;h.X=i.x=b;h.Y=i.y=c;h.W=i.width=d;h.H=i.height=e;i.r=f;i.path=g;h.type="rect";return h};bR=function(a,b,c,d,e){var f=cd("group"),g=cd("oval"),h=g.style;f.style.cssText="position:absolute;left:0;top:0;width:"+a.width+"px;height:"+a.height+"px";f.coordsize=b$;f.coordorigin=a.coordorigin;f[l](g);var i=new bN(g,f,a);i.type="ellipse";bK(i,{stroke:"#000"});i.attrs.cx=b;i.attrs.cy=c;i.attrs.rx=d;i.attrs.ry=e;i.setBox({x:b-d,y:c-e,width:d*2,height:e*2});a.canvas[l](f);return i};bS=function(a,b,c,d,e,f){var g=cd("group"),h=cd("image");g.style.cssText="position:absolute;left:0;top:0;width:"+a.width+"px;height:"+a.height+"px";g.coordsize=b$;g.coordorigin=a.coordorigin;h.src=b;g[l](h);var i=new bN(h,g,a);i.type="image";i.attrs.src=b;i.attrs.x=c;i.attrs.y=d;i.attrs.w=e;i.attrs.h=f;i.setBox({x:c,y:d,width:e,height:f});a.canvas[l](g);return i};bT=function(b,c,d,e){var f=cd("group"),g=cd("shape"),h=g.style,i=cd("path"),j=i.style,k=cd("textpath");f.style.cssText="position:absolute;left:0;top:0;width:"+b.width+"px;height:"+b.height+"px";f.coordsize=b$;f.coordorigin=b.coordorigin;i.v=a.format("m{0},{1}l{2},{1}",Q(c*10),Q(d*10),Q(c*10)+1);i.textpathok=true;h.width=b.width;h.height=b.height;k.string=r(e);k.on=true;g[l](k);g[l](i);f[l](g);var m=new bN(k,f,b);m.shape=g;m.textpath=i;m.type="text";m.attrs.text=e;m.attrs.x=c;m.attrs.y=d;m.attrs.w=1;m.attrs.h=1;bK(m,{font:W.font,stroke:"none",fill:"#000"});m.setBox();b.canvas[l](f);return m};bU=function(a,b){var c=this.canvas.style;a==+a&&(a+="px");b==+b&&(b+="px");c.width=a;c.height=b;c.clip="rect(0 "+a+" "+b+" 0)";return this};var cd;g.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)");try{!g.namespaces.rvml&&g.namespaces.add("rvml","urn:schemas-microsoft-com:vml");cd=function(a){return g.createElement("<rvml:"+a+" class=\"rvml\">")}}catch(a){cd=function(a){return g.createElement("<"+a+" xmlns=\"urn:schemas-microsoft.com:vml\" class=\"rvml\">")}}bV=function(){var b=by[m](0,arguments),c=b.container,d=b.height,e,f=b.width,h=b.x,i=b.y;if(!c)throw new Error("VML container not found.");var k=new j,n=k.canvas=g.createElement("div"),o=n.style;h=h||0;i=i||0;f=f||512;d=d||342;f==+f&&(f+="px");d==+d&&(d+="px");k.width=1000;k.height=1000;k.coordsize=b_*1000+q+b_*1000;k.coordorigin="0 0";k.span=g.createElement("span");k.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";n[l](k.span);o.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d);if(c==1){g.body[l](n);o.left=h+"px";o.top=i+"px";o.position="absolute"}else c.firstChild?c.insertBefore(n,c.firstChild):c[l](n);bz.call(k,k,a.fn);return k};k.clear=function(){this.canvas.innerHTML=p;this.span=g.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[l](this.span);this.bottom=this.top=null};k.remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=bF(a);return true}}var ce=navigator.userAgent.match(/Version\\x2f(.*?)\s/);navigator.vendor=="Apple Computer, Inc."&&(ce&&ce[1]<4||navigator.platform.slice(0,2)=="iP")?k.safari=function(){var a=this.rect(-99,-99,this.width+99,this.height+99).attr({stroke:"none"});h.setTimeout(function(){a.remove()})}:k.safari=function(){};var cf=function(){this.returnValue=false},cg=function(){return this.originalEvent.preventDefault()},ch=function(){this.cancelBubble=true},ci=function(){return this.originalEvent.stopPropagation()},cj=(function(){{if(g.addEventListener)return function(a,b,c,d){var e=o&&u[b]?u[b]:b,g=function(e){if(o&&u[f](b))for(var g=0,h=e.targetTouches&&e.targetTouches.length;g<h;g++){if(e.targetTouches[g].target==a){var i=e;e=e.targetTouches[g];e.originalEvent=i;e.preventDefault=cg;e.stopPropagation=ci;break}}return c.call(d,e)};a.addEventListener(e,g,false);return function(){a.removeEventListener(e,g,false);return true}};if(g.attachEvent)return function(a,b,c,d){var e=function(a){a=a||h.event;a.preventDefault=a.preventDefault||cf;a.stopPropagation=a.stopPropagation||ch;return c.call(d,a)};a.attachEvent("on"+b,e);var f=function(){a.detachEvent("on"+b,e);return true};return f}}})(),ck=[],cl=function(a){var b=a.clientX,c=a.clientY,d=g.documentElement.scrollTop||g.body.scrollTop,e=g.documentElement.scrollLeft||g.body.scrollLeft,f,h=ck.length;while(h--){f=ck[h];if(o){var i=a.touches.length,j;while(i--){j=a.touches[i];if(j.identifier==f.el._drag.id){b=j.clientX;c=j.clientY;(a.originalEvent?a.originalEvent:a).preventDefault();break}}}else a.preventDefault();b+=e;c+=d;f.move&&f.move.call(f.move_scope||f.el,b-f.el._drag.x,c-f.el._drag.y,b,c,a)}},cm=function(b){a.unmousemove(cl).unmouseup(cm);var c=ck.length,d;while(c--){d=ck[c];d.el._drag={};d.end&&d.end.call(d.end_scope||d.start_scope||d.move_scope||d.el,b)}ck=[]};for(var cn=t[w];cn--;)(function(b){a[b]=bN[e][b]=function(c,d){if(a.is(c,"function")){this.events=this.events||[];this.events.push({name:b,f:c,unbind:cj(this.shape||this.node||g,b,c,d||this)})}return this};a["un"+b]=bN[e]["un"+b]=function(a){var c=this.events,d=c[w];while(d--)if(c[d].name==b&&c[d].f==a){c[d].unbind();c.splice(d,1);!c.length&&delete this.events;return this}return this}})(t[cn]);bO.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)};bO.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};bO.drag=function(b,c,d,e,f,h){this._drag={};this.mousedown(function(i){(i.originalEvent||i).preventDefault();var j=g.documentElement.scrollTop||g.body.scrollTop,k=g.documentElement.scrollLeft||g.body.scrollLeft;this._drag.x=i.clientX+k;this._drag.y=i.clientY+j;this._drag.id=i.identifier;c&&c.call(f||e||this,i.clientX+k,i.clientY+j,i);!ck.length&&a.mousemove(cl).mouseup(cm);ck.push({el:this,move:b,end:d,move_scope:e,start_scope:f,end_scope:h})});return this};bO.undrag=function(b,c,d){var e=ck.length;while(e--)ck[e].el==this&&(ck[e].move==b&&ck[e].end==d)&&ck.splice(e++,1);!ck.length&&a.unmousemove(cl).unmouseup(cm)};k.circle=function(a,b,c){return bP(this,a||0,b||0,c||0)};k.rect=function(a,b,c,d,e){return bQ(this,a||0,b||0,c||0,d||0,e||0)};k.ellipse=function(a,b,c,d){return bR(this,a||0,b||0,c||0,d||0)};k.path=function(b){b&&!a.is(b,F)&&!a.is(b[0],G)&&(b+=p);return bH(a.format[m](a,arguments),this)};k.image=function(a,b,c,d,e){return bS(this,a||"about:blank",b||0,c||0,d||0,e||0)};k.text=function(a,b,c){return bT(this,a||0,b||0,r(c))};k.set=function(a){arguments[w]>1&&(a=Array[e].splice.call(arguments,0,arguments[w]));return new cC(a)};k.setSize=bU;k.top=k.bottom=null;k.raphael=a;function co(){return this.x+q+this.y}bO.resetScale=function(){if(this.removed)return this;this._.sx=1;this._.sy=1;this.attrs.scale="1 1"};bO.scale=function(a,b,c,d){if(this.removed)return this;if(a==null&&b==null)return{x:this._.sx,y:this._.sy,toString:co};b=b||a;!(+b)&&(b=a);var e,f,g,h,i=this.attrs;if(a!=0){var j=this.getBBox(),k=j.x+j.width/2,l=j.y+j.height/2,m=B(a/this._.sx),o=B(b/this._.sy);c=+c||c==0?c:k;d=+d||d==0?d:l;var r=this._.sx>0,s=this._.sy>0,t=~(~(a/B(a))),u=~(~(b/B(b))),x=m*t,y=o*u,z=this.node.style,A=c+B(k-c)*x*(k>c==r?1:-1),C=d+B(l-d)*y*(l>d==s?1:-1),D=a*t>b*u?o:m;switch(this.type){case"rect":case"image":var E=i.width*m,F=i.height*o;this.attr({height:F,r:i.r*D,width:E,x:A-E/2,y:C-F/2});break;case"circle":case"ellipse":this.attr({rx:i.rx*m,ry:i.ry*o,r:i.r*D,cx:A,cy:C});break;case"text":this.attr({x:A,y:C});break;case"path":var G=bp(i.path),H=true,I=r?x:m,J=s?y:o;for(var K=0,L=G[w];K<L;K++){var M=G[K],N=V.call(M[0]);{if(N=="M"&&H)continue;H=false}if(N=="A"){M[G[K][w]-2]*=I;M[G[K][w]-1]*=J;M[1]*=m;M[2]*=o;M[5]=+(t+u?!(!(+M[5])):!(+M[5]))}else if(N=="H")for(var O=1,P=M[w];O<P;O++)M[O]*=I;else if(N=="V")for(O=1,P=M[w];O<P;O++)M[O]*=J;else for(O=1,P=M[w];O<P;O++)M[O]*=O%2?I:J}var Q=bn(G);e=A-Q.x-Q.width/2;f=C-Q.y-Q.height/2;G[0][1]+=e;G[0][2]+=f;this.attr({path:G});break}if(this.type in{text:1,image:1}&&(t!=1||u!=1))if(this.transformations){this.transformations[2]="scale("[n](t,",",u,")");this.node[R]("transform",this.transformations[v](q));e=t==-1?-i.x-(E||0):i.x;f=u==-1?-i.y-(F||0):i.y;this.attr({x:e,y:f});i.fx=t-1;i.fy=u-1}else{this.node.filterMatrix=U+".Matrix(M11="[n](t,", M12=0, M21=0, M22=",u,", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')");z.filter=(this.node.filterMatrix||p)+(this.node.filterOpacity||p)}else if(this.transformations){this.transformations[2]=p;this.node[R]("transform",this.transformations[v](q));i.fx=0;i.fy=0}else{this.node.filterMatrix=p;z.filter=(this.node.filterMatrix||p)+(this.node.filterOpacity||p)}i.scale=[a,b,c,d][v](q);this._.sx=a;this._.sy=b}return this};bO.clone=function(){if(this.removed)return null;var a=this.attr();delete a.scale;delete a.translation;return this.paper[this.type]().attr(a)};var cp={},cq=function(b,c,d,e,f,g,h,i,j){var k=0,l=100,m=[b,c,d,e,f,g,h,i].join(),n=cp[m],o,p;!n&&(cp[m]=n={data:[]});n.timer&&clearTimeout(n.timer);n.timer=setTimeout(function(){delete cp[m]},2000);if(j!=null){var q=cq(b,c,d,e,f,g,h,i);l=~(~q)*10}for(var r=0;r<l+1;r++){if(n.data[j]>r)p=n.data[r*l];else{p=a.findDotsAtSegment(b,c,d,e,f,g,h,i,r/l);n.data[r]=p}r&&(k+=C(C(o.x-p.x,2)+C(o.y-p.y,2),0.5));if(j!=null&&k>=j)return p;o=p}if(j==null)return k},cr=function(b,c){return function(d,e,f){d=bw(d);var g,h,i,j,k="",l={},m,n=0;for(var o=0,p=d.length;o<p;o++){i=d[o];if(i[0]=="M"){g=+i[1];h=+i[2]}else{j=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6]);if(n+j>e){if(c&&!l.start){m=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);k+=["C",m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k;k=["M",m.x,m.y+"C",m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]][v]();n+=j;g=+i[5];h=+i[6];continue}if(!b&&!c){m=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j;g=+i[5];h=+i[6]}k+=i}l.end=k;m=b?n:c?l:a.findDotsAtSegment(g,h,i[1],i[2],i[3],i[4],i[5],i[6],1);m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},cs=cr(1),ct=cr(),cu=cr(0,1);bO.getTotalLength=function(){if(this.type!="path")return;if(this.node.getTotalLength)return this.node.getTotalLength();return cs(this.attrs.path)};bO.getPointAtLength=function(a){if(this.type!="path")return;return ct(this.attrs.path,a)};bO.getSubpath=function(a,b){if(this.type!="path")return;if(B(this.getTotalLength()-b)<"1e-6")return cu(this.attrs.path,a).end;var c=cu(this.attrs.path,b,1);return a?cu(c,a).end:c};a.easing_formulas={linear:function(a){return a},"<":function(a){return C(a,3)},">":function(a){return C(a-1,3)+1},"<>":function(a){a=a*2;if(a<1)return C(a,3)/2;a-=2;return(C(a,3)+2)/2},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==0||a==1)return a;var b=0.3,c=b/4;return C(2,-10*a)*y.sin((a-c)*(2*D)/b)+1},bounce:function(a){var b=7.5625,c=2.75,d;if(a<1/c)d=b*a*a;else if(a<2/c){a-=1.5/c;d=b*a*a+0.75}else if(a<2.5/c){a-=2.25/c;d=b*a*a+0.9375}else{a-=2.625/c;d=b*a*a+0.984375}return d}};var cv=[],cw=function(){var b=+(new Date);for(var c=0;c<cv[w];c++){var d=cv[c];if(d.stop||d.el.removed)continue;var e=b-d.start,g=d.ms,h=d.easing,i=d.from,j=d.diff,k=d.to,l=d.t,m=d.el,n={},o;if(e<g){var r=h(e/g);for(var s in i)if(i[f](s)){switch(X[s]){case"along":o=r*g*j[s];k.back&&(o=k.len-o);var t=ct(k[s],o);m.translate(j.sx-j.x||0,j.sy-j.y||0);j.x=t.x;j.y=t.y;m.translate(t.x-j.sx,t.y-j.sy);k.rot&&m.rotate(j.r+t.alpha,t.x,t.y);break;case E:o=+i[s]+r*g*j[s];break;case"colour":o="rgb("+[cy(Q(i[s].r+r*g*j[s].r)),cy(Q(i[s].g+r*g*j[s].g)),cy(Q(i[s].b+r*g*j[s].b))][v](",")+")";break;case"path":o=[];for(var u=0,x=i[s][w];u<x;u++){o[u]=[i[s][u][0]];for(var y=1,z=i[s][u][w];y<z;y++)o[u][y]=+i[s][u][y]+r*g*j[s][u][y];o[u]=o[u][v](q)}o=o[v](q);break;case"csv":switch(s){case"translation":var A=r*g*j[s][0]-l.x,B=r*g*j[s][1]-l.y;l.x+=A;l.y+=B;o=A+q+B;break;case"rotation":o=+i[s][0]+r*g*j[s][0];i[s][1]&&(o+=","+i[s][1]+","+i[s][2]);break;case"scale":o=[+i[s][0]+r*g*j[s][0],+i[s][1]+r*g*j[s][1],2 in k[s]?k[s][2]:p,3 in k[s]?k[s][3]:p][v](q);break;case"clip-rect":o=[];u=4;while(u--)o[u]=+i[s][u]+r*g*j[s][u];break}break;default:var C=[].concat(i[s]);o=[];u=m.paper.customAttributes[s].length;while(u--)o[u]=+C[u]+r*g*j[s][u];break}n[s]=o}m.attr(n);m._run&&m._run.call(m)}else{if(k.along){t=ct(k.along,k.len*!k.back);m.translate(j.sx-(j.x||0)+t.x-j.sx,j.sy-(j.y||0)+t.y-j.sy);k.rot&&m.rotate(j.r+t.alpha,t.x,t.y)}(l.x||l.y)&&m.translate(-l.x,-l.y);k.scale&&(k.scale+=p);m.attr(k);cv.splice(c--,1)}}a.svg&&m&&m.paper&&m.paper.safari();cv[w]&&setTimeout(cw)},cx=function(b,c,d,e,f){var g=d-e;c.timeouts.push(setTimeout(function(){a.is(f,"function")&&f.call(c);c.animate(b,g,b.easing)},e))},cy=function(a){return z(A(a,255),0)},cz=function(a,b){if(a==null)return{x:this._.tx,y:this._.ty,toString:co};this._.tx+=+a;this._.ty+=+b;switch(this.type){case"circle":case"ellipse":this.attr({cx:+a+this.attrs.cx,cy:+b+this.attrs.cy});break;case"rect":case"image":case"text":this.attr({x:+a+this.attrs.x,y:+b+this.attrs.y});break;case"path":var c=bp(this.attrs.path);c[0][1]+=+a;c[0][2]+=+b;this.attr({path:c});break}return this};bO.animateWith=function(a,b,c,d,e){for(var f=0,g=cv.length;f<g;f++)cv[f].el.id==a.id&&(b.start=cv[f].start);return this.animate(b,c,d,e)};bO.animateAlong=cA();bO.animateAlongBack=cA(1);function cA(b){return function(c,d,e,f){var g={back:b};a.is(e,"function")?f=e:g.rot=e;c&&c.constructor==bN&&(c=c.attrs.path);c&&(g.along=c);return this.animate(g,d,f)}}function cB(a,b,c,d,e,f){var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;function m(a){return((i*a+h)*a+g)*a}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function o(a,b){var c,d,e,f,j,k;for(e=a,k=0;k<8;k++){f=m(e)-a;if(B(f)<b)return e;j=(3*i*e+2*h)*e+g;if(B(j)<0.000001)break;e=e-f/j}c=0;d=1;e=a;if(e<c)return c;if(e>d)return d;while(c<d){f=m(e);if(B(f-a)<b)return e;a>f?c=e:d=e;e=(d-c)/2+c}return e}return n(a,1/(200*f))}bO.onAnimation=function(a){this._run=a||0;return this};bO.animate=function(c,d,e,g){var h=this;h.timeouts=h.timeouts||[];if(a.is(e,"function")||!e)g=e||null;if(h.removed){g&&g.call(h);return h}var i={},j={},k=false,l={};for(var m in c)if(c[f](m)){if(X[f](m)||h.paper.customAttributes[f](m)){k=true;i[m]=h.attr(m);i[m]==null&&(i[m]=W[m]);j[m]=c[m];switch(X[m]){case"along":var n=cs(c[m]),o=ct(c[m],n*!(!c.back)),p=h.getBBox();l[m]=n/d;l.tx=p.x;l.ty=p.y;l.sx=o.x;l.sy=o.y;j.rot=c.rot;j.back=c.back;j.len=n;c.rot&&(l.r=S(h.rotate())||0);break;case E:l[m]=(j[m]-i[m])/d;break;case"colour":i[m]=a.getRGB(i[m]);var q=a.getRGB(j[m]);l[m]={r:(q.r-i[m].r)/d,g:(q.g-i[m].g)/d,b:(q.b-i[m].b)/d};break;case"path":var t=bw(i[m],j[m]);i[m]=t[0];var u=t[1];l[m]=[];for(var v=0,x=i[m][w];v<x;v++){l[m][v]=[0];for(var y=1,z=i[m][v][w];y<z;y++)l[m][v][y]=(u[v][y]-i[m][v][y])/d}break;case"csv":var A=r(c[m])[s](b),B=r(i[m])[s](b);switch(m){case"translation":i[m]=[0,0];l[m]=[A[0]/d,A[1]/d];break;case"rotation":i[m]=B[1]==A[1]&&B[2]==A[2]?B:[0,A[1],A[2]];l[m]=[(A[0]-i[m][0])/d,0,0];break;case"scale":c[m]=A;i[m]=r(i[m])[s](b);l[m]=[(A[0]-i[m][0])/d,(A[1]-i[m][1])/d,0,0];break;case"clip-rect":i[m]=r(i[m])[s](b);l[m]=[];v=4;while(v--)l[m][v]=(A[v]-i[m][v])/d;break}j[m]=A;break;default:A=[].concat(c[m]);B=[].concat(i[m]);l[m]=[];v=h.paper.customAttributes[m][w];while(v--)l[m][v]=((A[v]||0)-(B[v]||0))/d;break}}}if(k){var G=a.easing_formulas[e];if(!G){G=r(e).match(P);if(G&&G[w]==5){var H=G;G=function(a){return cB(a,+H[1],+H[2],+H[3],+H[4],d)}}else G=function(a){return a}}cv.push({start:c.start||+(new Date),ms:d,easing:G,from:i,diff:l,to:j,el:h,t:{x:0,y:0}});a.is(g,"function")&&(h._ac=setTimeout(function(){g.call(h)},d));cv[w]==1&&setTimeout(cw)}else{var C=[],D;for(var F in c)if(c[f](F)&&Z.test(F)){m={value:c[F]};F=="from"&&(F=0);F=="to"&&(F=100);m.key=T(F,10);C.push(m)}C.sort(be);C[0].key&&C.unshift({key:0,value:h.attrs});for(v=0,x=C[w];v<x;v++)cx(C[v].value,h,d/100*C[v].key,d/100*(C[v-1]&&C[v-1].key||0),C[v-1]&&C[v-1].value.callback);D=C[C[w]-1].value.callback;D&&h.timeouts.push(setTimeout(function(){D.call(h)},d))}return this};bO.stop=function(){for(var a=0;a<cv.length;a++)cv[a].el.id==this.id&&cv.splice(a--,1);for(a=0,ii=this.timeouts&&this.timeouts.length;a<ii;a++)clearTimeout(this.timeouts[a]);this.timeouts=[];clearTimeout(this._ac);delete this._ac;return this};bO.translate=function(a,b){return this.attr({translation:a+" "+b})};bO[H]=function(){return"Rapha├лlтАЩs object"};a.ae=cv;var cC=function(a){this.items=[];this[w]=0;this.type="set";if(a)for(var b=0,c=a[w];b<c;b++){if(a[b]&&(a[b].constructor==bN||a[b].constructor==cC)){this[this.items[w]]=this.items[this.items[w]]=a[b];this[w]++}}};cC[e][L]=function(){var a,b;for(var c=0,d=arguments[w];c<d;c++){a=arguments[c];if(a&&(a.constructor==bN||a.constructor==cC)){b=this.items[w];this[b]=this.items[b]=a;this[w]++}}return this};cC[e].pop=function(){delete this[this[w]--];return this.items.pop()};for(var cD in bO)bO[f](cD)&&(cC[e][cD]=(function(a){return function(){for(var b=0,c=this.items[w];b<c;b++)this.items[b][a][m](this.items[b],arguments);return this}})(cD));cC[e].attr=function(b,c){if(b&&a.is(b,G)&&a.is(b[0],"object"))for(var d=0,e=b[w];d<e;d++)this.items[d].attr(b[d]);else for(var f=0,g=this.items[w];f<g;f++)this.items[f].attr(b,c);return this};cC[e].animate=function(b,c,d,e){(a.is(d,"function")||!d)&&(e=d||null);var f=this.items[w],g=f,h,i=this,j;e&&(j=function(){!(--f)&&e.call(i)});d=a.is(d,F)?d:j;h=this.items[--g].animate(b,c,d,j);while(g--)this.items[g]&&!this.items[g].removed&&this.items[g].animateWith(h,b,c,d,j);return this};cC[e].insertAfter=function(a){var b=this.items[w];while(b--)this.items[b].insertAfter(a);return this};cC[e].getBBox=function(){var a=[],b=[],c=[],d=[];for(var e=this.items[w];e--;){var f=this.items[e].getBBox();a[L](f.x);b[L](f.y);c[L](f.x+f.width);d[L](f.y+f.height)}a=A[m](0,a);b=A[m](0,b);return{x:a,y:b,width:z[m](0,c)-a,height:z[m](0,d)-b}};cC[e].clone=function(a){a=new cC;for(var b=0,c=this.items[w];b<c;b++)a[L](this.items[b].clone());return a};a.registerFont=function(a){if(!a.face)return a;this.fonts=this.fonts||{};var b={w:a.w,face:{},glyphs:{}},c=a.face["font-family"];for(var d in a.face)a.face[f](d)&&(b.face[d]=a.face[d]);this.fonts[c]?this.fonts[c][L](b):this.fonts[c]=[b];if(!a.svg){b.face["units-per-em"]=T(a.face["units-per-em"],10);for(var e in a.glyphs)if(a.glyphs[f](e)){var g=a.glyphs[e];b.glyphs[e]={w:g.w,k:{},d:g.d&&"M"+g.d[Y](/[mlcxtrv]/g,function(a){return({l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"})[a]||"M"})+"z"};if(g.k)for(var h in g.k)g[f](h)&&(b.glyphs[e].k[h]=g.k[h])}}return a};k.getFont=function(b,c,d,e){e=e||"normal";d=d||"normal";c=+c||({normal:400,bold:700,lighter:300,bolder:800})[c]||400;if(!a.fonts)return;var g=a.fonts[b];if(!g){var h=new RegExp("(^|\\s)"+b[Y](/[^\w\d\s+!~.:_-]/g,p)+"(\\s|$)","i");for(var i in a.fonts)if(a.fonts[f](i)){if(h.test(i)){g=a.fonts[i];break}}}var j;if(g)for(var k=0,l=g[w];k<l;k++){j=g[k];if(j.face["font-weight"]==c&&(j.face["font-style"]==d||!j.face["font-style"])&&j.face["font-stretch"]==e)break}return j};k.print=function(c,d,e,f,g,h,i){h=h||"middle";i=z(A(i||0,1),-1);var j=this.set(),k=r(e)[s](p),l=0,m=p,n;a.is(f,e)&&(f=this.getFont(f));if(f){n=(g||16)/f.face["units-per-em"];var o=f.face.bbox.split(b),q=+o[0],t=+o[1]+(h=="baseline"?o[3]-o[1]+ +f.face.descent:(o[3]-o[1])/2);for(var u=0,v=k[w];u<v;u++){var x=u&&f.glyphs[k[u-1]]||{},y=f.glyphs[k[u]];l+=u?(x.w||f.w)+(x.k&&x.k[k[u]]||0)+f.w*i:0;y&&y.d&&j[L](this.path(y.d).attr({fill:"#000",stroke:"none",translation:[l,0]}))}j.scale(n,n,q,t).translate(c-q,d-t)}return j};a.format=function(b,c){var e=a.is(c,G)?[0][n](c):arguments;b&&a.is(b,F)&&e[w]-1&&(b=b[Y](d,function(a,b){return e[++b]==null?p:e[b]}));return b||p};a.ninja=function(){i.was?h.Raphael=i.is:delete Raphael;return a};a.el=bO;a.st=cC[e];i.was?h.Raphael=a:Raphael=a})()
lib/Aitoc/Aitloadmon/Adapter/Apc.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Adapter_Apc
7
+ {
8
+ private static $DATA_KEY = 'aitloadmon_data';
9
+ private static $DATA_NUM_KEY = 'aitloadmon_data_key';
10
+
11
+ /**
12
+ * Saves the data into storage
13
+ *
14
+ * @param array $params
15
+ */
16
+ public function save($params)
17
+ {
18
+ if(!$index = apc_fetch(self::$DATA_NUM_KEY))
19
+ {
20
+ apc_add(self::$DATA_NUM_KEY, 1);
21
+ $index = 1;
22
+ }
23
+
24
+ while(!apc_add(self::$DATA_KEY.$index,$params))
25
+ {
26
+ $index++;
27
+ }
28
+ apc_delete(self::$DATA_NUM_KEY);
29
+ apc_add(self::$DATA_NUM_KEY, $index);
30
+ }
31
+
32
+
33
+ /**
34
+ * Gets the data from the storage
35
+ *
36
+ * @return array
37
+ */
38
+ public function getData()
39
+ {
40
+ $data = array();
41
+ if($index = apc_fetch(self::$DATA_NUM_KEY))
42
+ {
43
+ for($i=1;$i<=$index;$i++)
44
+ {
45
+ if($element = apc_fetch(self::$DATA_KEY.$i))
46
+ {
47
+ $data = array_merge_recursive($data,$element);
48
+ apc_delete(self::$DATA_KEY.$i);
49
+ }
50
+ }
51
+ apc_delete(self::$DATA_NUM_KEY);
52
+ apc_add(self::$DATA_NUM_KEY, $i);
53
+ }
54
+ return $data;
55
+ }
56
+ }
lib/Aitoc/Aitloadmon/Adapter/File.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Adapter_File
7
+ {
8
+ const DS = DIRECTORY_SEPARATOR;
9
+
10
+ private static $_dataDir = 'data';
11
+ private static $_filename = 'data_';
12
+
13
+ /**
14
+ * Gets the path to the directory for the data files
15
+ *
16
+ * @return string
17
+ */
18
+ private function _getDataDir()
19
+ {
20
+ return Aitoc_Aitloadmon_Collect::getTempDir().self::$_dataDir.self::DS;
21
+ }
22
+
23
+ /**
24
+ * Returns the file resource for writing
25
+ *
26
+ * @return resource
27
+ */
28
+ private function _getCurrentFileHandle()
29
+ {
30
+ $i = 0;
31
+ $currentFileHandle = null;
32
+ if(!file_exists($this->_getDataDir()))
33
+ {
34
+ mkdir($this->_getDataDir());
35
+ }
36
+ $curFilePath = $this->_getDataDir().self::$_filename;
37
+ while(!$currentFileHandle)
38
+ {
39
+ $fileHandle = fopen($curFilePath.$i, 'a');
40
+ $locked = flock($fileHandle, LOCK_EX);
41
+ if($locked)
42
+ {
43
+ $currentFileHandle = $fileHandle;
44
+ }
45
+ $i++;
46
+ }
47
+ return $currentFileHandle;
48
+ }
49
+
50
+ /**
51
+ * Writes a string to file
52
+ *
53
+ * @param string $string
54
+ */
55
+ private function _writeStringToFile($string)
56
+ {
57
+ $fileHandle = $this->_getCurrentFileHandle();
58
+ fwrite($fileHandle,$string."\r\n");
59
+ flock($fileHandle, LOCK_UN);
60
+ fclose($fileHandle);
61
+ }
62
+
63
+ /**
64
+ * Saves the data into storage
65
+ *
66
+ * @param array $params
67
+ */
68
+ public function save($params)
69
+ {
70
+ $this->_writeStringToFile(serialize($params));
71
+ }
72
+
73
+ /**
74
+ * Gets the list of data files
75
+ *
76
+ * @return array
77
+ */
78
+ private function _getDataFileList()
79
+ {
80
+ return scandir($this->_getDataDir());
81
+ }
82
+
83
+ /**
84
+ * Gets the data from the storage
85
+ *
86
+ * @return array
87
+ */
88
+ public function getData()
89
+ {
90
+ $dir = $this->_getDataDir();
91
+ $data = array();
92
+ foreach($this->_getDataFileList() as $fileName)
93
+ {
94
+ if($fileName != '.' && $fileName != '..')
95
+ {
96
+ $fileHandle = fopen($dir.$fileName, 'r');
97
+ $locked = null;
98
+ while(!$locked)
99
+ {
100
+ $locked = flock($fileHandle, LOCK_EX);
101
+ }
102
+
103
+ while($string = fgets($fileHandle))
104
+ {
105
+ $data = array_merge_recursive($data,unserialize($string));
106
+ }
107
+ flock($fileHandle, LOCK_UN);
108
+ fclose($fileHandle);
109
+ unlink($dir.$fileName);
110
+ }
111
+ }
112
+
113
+ return $data;
114
+ }
115
+
116
+ }
lib/Aitoc/Aitloadmon/Adapter/Memcached.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright Copyright (c) 2013 AITOC, Inc.
4
+ * @license License agreement could be found at the root folder of this package
5
+ */
6
+ class Aitoc_Aitloadmon_Adapter_Memcached
7
+ {
8
+ private static $DATA_KEY = 'aitloadmon_data';
9
+ private static $DATA_NUM_KEY = 'aitloadmon_data_key';
10
+
11
+ private $_cache;
12
+
13
+ /**
14
+ * Initiates the connection to memcache
15
+ *
16
+ * @param array $settings
17
+ */
18
+ public function __construct($settings)
19
+ {
20
+ $this->_cache = new Memcache;
21
+ $this->_cache->connect($settings['host'], $settings['port']);
22
+ }
23
+
24
+ /**
25
+ * Saves the data into storage
26
+ *
27
+ * @param array $params
28
+ */
29
+ public function save($params)
30
+ {
31
+ if(!$index = $this->_cache->get(self::$DATA_NUM_KEY))
32
+ {
33
+ $this->_cache->set(self::$DATA_NUM_KEY, 1);
34
+ $index = 1;
35
+ }
36
+
37
+ while(!$this->_cache->add(self::$DATA_KEY.$index,$params))
38
+ {
39
+ $index++;
40
+ }
41
+ $this->_cache->set(self::$DATA_NUM_KEY, $index);
42
+ }
43
+
44
+ /**
45
+ * Gets the data from the storage
46
+ *
47
+ * @return array
48
+ */
49
+ public function getData()
50
+ {
51
+ $data = array();
52
+ if($index = $this->_cache->get(self::$DATA_NUM_KEY))
53
+ {
54
+ for($i=1;$i<=$index;$i++)
55
+ {
56
+ if($element = $this->_cache->get(self::$DATA_KEY.$i))
57
+ {
58
+ $data = array_merge_recursive($data,$element);
59
+ $this->_cache->delete(self::$DATA_KEY.$i);
60
+ }
61
+ }
62
+ $this->_cache->set(self::$DATA_NUM_KEY, 1);
63
+ }
64
+ return $data;
65
+ }
66
+ }
lib/Aitoc/Aitloadmon/Collect.php ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Aitoc_Aitloadmon_Collect
4
+ {
5
+ const DS = DIRECTORY_SEPARATOR;
6
+
7
+ private static $_tempDir = 'ait_loadmon';
8
+ private static $_settingsFilename = 'settings.ini';
9
+ private static $_loadLevelFilename = 'load_level';
10
+
11
+ private static $_adapter;
12
+ private static $_enabled;
13
+ private static $_loadLevel;
14
+ private static $_pathAddon;
15
+ private $_uniqueId;
16
+ private $_startWritten;
17
+
18
+ /**
19
+ * Gets the root path of Magento installation
20
+ *
21
+ * @return string
22
+ */
23
+ private static function _getRootPath()
24
+ {//todo think of it
25
+ return dirname($_SERVER['SCRIPT_FILENAME']).self::$_pathAddon;
26
+ }
27
+
28
+ /**
29
+ * Returns whether the module is enabled
30
+ *
31
+ * @return bool
32
+ */
33
+ public static function isModuleEnabled()
34
+ {
35
+ if(!isset(self::$_enabled))
36
+ {
37
+ self::$_enabled = false;
38
+ $config = simplexml_load_file(self::_getRootPath().self::DS.'app'.self::DS.'etc'.self::DS.'modules'.self::DS.'Aitoc_Aitloadmon.xml');
39
+ if ($config){
40
+ self::$_enabled = (string)$config->modules->Aitoc_Aitloadmon->active;
41
+ }
42
+ }
43
+ return self::$_enabled;
44
+ }
45
+
46
+
47
+ /**
48
+ * Checks if module is enabled and if it is writes the start data
49
+ */
50
+ public function __construct($pathAddon = '')
51
+ {
52
+ self::$_pathAddon = $pathAddon;
53
+ if (!self::isModuleEnabled())
54
+ {
55
+ return;
56
+ }
57
+ $this->_startWritten = 0;
58
+ $this->_save();
59
+ register_shutdown_function(array($this,'shutdown'));
60
+ }
61
+
62
+ /**
63
+ * Gets the unique id for storing purposes
64
+ *
65
+ * @return string
66
+ */
67
+ private function _getUniqueId()
68
+ {
69
+ if(!isset($this->_uniqueId))
70
+ {
71
+ $this->_uniqueId = uniqid();
72
+ }
73
+ return $this->_uniqueId;
74
+ }
75
+
76
+ /**
77
+ * Gets the VAR folder of Magento
78
+ *
79
+ * @return string
80
+ */
81
+ public static function getTempDir()
82
+ {
83
+ return self::_getRootPath().self::DS.'var'.self::DS.self::$_tempDir.self::DS;
84
+ }
85
+
86
+ /**
87
+ * Gets the full path of the settings file
88
+ *
89
+ * @return string
90
+ */
91
+ private static function _getSettingsFilename()
92
+ {
93
+ return self::getTempDir().self::$_settingsFilename;
94
+ }
95
+
96
+ /**
97
+ * Saves the data
98
+ */
99
+ private function _save()
100
+ {
101
+ if(!$this->_startWritten)
102
+ {
103
+ $saveData = array(
104
+ $this->_getUniqueId() => array(
105
+ 'request_uri' => $_SERVER['REQUEST_URI'],
106
+ 'start' => microtime(true),
107
+ )
108
+ );
109
+ }
110
+ else
111
+ {
112
+ $saveData = array(
113
+ $this->_getUniqueId() => array(
114
+ 'end' => microtime(true),
115
+ )
116
+ );
117
+ }
118
+ self::_getAdapter()->save($saveData);
119
+ $this->_startWritten = 1;
120
+ }
121
+
122
+ /**
123
+ * Checks if module is enabled and if it is writes the finish data
124
+ */
125
+ public function shutdown()/*__destruct()*/
126
+ {
127
+ if (!self::isModuleEnabled())
128
+ {
129
+ return;
130
+ }
131
+ $this->_save();
132
+ }
133
+
134
+ /**
135
+ * Gets the data storage adapter
136
+ *
137
+ * @return Aitoc_Aitloadmon_Adapter_Memcached|Aitoc_Aitloadmon_Adapter_File|Aitoc_Aitloadmon_Adapter_Apc
138
+ * @throws Exception
139
+ */
140
+ private static function _getAdapter()
141
+ {
142
+ if(!isset(self::$_adapter))
143
+ {
144
+ $settings = self::_getSettings();
145
+
146
+ if(!isset($settings['adapter']))
147
+ {
148
+ throw new Exception('No adapter specified in settings');
149
+ }
150
+
151
+ $adapterName = ucfirst(strtolower($settings['adapter']));
152
+ $adapterClassFile = self::_getRootPath().self::DS.'lib'.self::DS.'Aitoc'.self::DS.'Aitloadmon'.self::DS.'Adapter'.self::DS.$adapterName.'.php';
153
+
154
+ if(!file_exists($adapterClassFile))
155
+ {
156
+ throw new Exception('Adapter does not exists');
157
+ }
158
+
159
+ include_once($adapterClassFile);
160
+
161
+ $adapterClass = 'Aitoc_Aitloadmon_Adapter_'.$adapterName;
162
+ self::$_adapter = new $adapterClass($settings);
163
+ }
164
+ return self::$_adapter;
165
+ }
166
+
167
+
168
+ /**
169
+ * Gets the settings array
170
+ *
171
+ * @return array
172
+ */
173
+ private static function _getSettings()
174
+ {
175
+ if(!file_exists(self::_getSettingsFilename()))
176
+ {
177
+
178
+ $config = simplexml_load_file(self::_getRootPath().self::DS.'app'.self::DS.'etc'.self::DS.'local.xml');
179
+ $cacheEngine = null;
180
+ if ($config){
181
+ $cacheEngine = (string)$config->global->cache->backend;
182
+ }
183
+ switch($cacheEngine)
184
+ {
185
+ case 'memcached':
186
+ $settingsString = 'adapter = "memcached"'."\r\n".'host = "'.(string)$config->global->cache->memcached->servers->server->host.'"'."\r\n".'port = "'.(string)$config->global->cache->memcached->servers->server->port.'"';
187
+ break;
188
+
189
+ case 'apc':
190
+ $settingsString = 'adapter = "apc"';
191
+ break;
192
+
193
+ default:
194
+ $settingsString = 'adapter = "file"';
195
+
196
+ }
197
+ if(!file_exists(self::getTempDir()))
198
+ {
199
+ mkdir(self::getTempDir());
200
+ }
201
+ file_put_contents(self::_getSettingsFilename(),$settingsString);
202
+
203
+ }
204
+
205
+ $settings = parse_ini_file(self::_getSettingsFilename());
206
+
207
+ return $settings;
208
+ }
209
+
210
+ /**
211
+ * Gets the data that was collected and stored
212
+ *
213
+ * @return array
214
+ */
215
+ public static function getData()
216
+ {
217
+ $adapter = self::_getAdapter();
218
+ $data = $adapter->getData();
219
+ return $data;
220
+ }
221
+
222
+ /**
223
+ * Gets the full path of a file with current load level
224
+ *
225
+ * @return string
226
+ */
227
+ private static function _getLoadLevelFilename()
228
+ {
229
+ return self::getTempDir().self::$_loadLevelFilename;
230
+ }
231
+
232
+ /**
233
+ * Sets current load level
234
+ *
235
+ * @param int $loadLevel
236
+ */
237
+ public static function setLoadLevel($loadLevel)
238
+ {
239
+ self::$_loadLevel = $loadLevel;
240
+ $filename = self::_getLoadLevelFilename();
241
+ file_put_contents($filename,$loadLevel);
242
+
243
+ }
244
+
245
+ /**
246
+ * Gets current load level
247
+ *
248
+ * @return null|int
249
+ */
250
+ public static function getLoadLevel()
251
+ {
252
+ if(!isset(self::$_loadLevel))
253
+ {
254
+ $filename = self::_getLoadLevelFilename();
255
+ if(file_exists($filename))
256
+ {
257
+ self::$_loadLevel = file_get_contents($filename);
258
+ }
259
+ else
260
+ {
261
+ self::$_loadLevel = null;
262
+ }
263
+ }
264
+ return self::$_loadLevel;
265
+ }
266
+ }
package.xml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>ServerLoadMonitorFree</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://www.aitoc.com/en/aitdownloadablefiles/download/aitfile/aitfile_id/722/"> License - proprietary (can be found in module package) </license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>It&#x2019;s quite frustrating to have your server overloaded or crashed when trade is brisk and sales are booming. Neither is it a good thing to happen to a fledging e-commerce company still busy building up credibility with customers. Crash prevention is a serious issue and a valid goal. And even if your server is powerful enough and properly customized it is still useful to keep an eye on it, monitor your Magento store traffic load page by page. &#xD;
10
+ &#xD;
11
+ Server Load Monitor is just the right extension to serve all the above mentioned purposes. It will provide you with a comprehensive overview of load-related processes essential for trouble-free performance of your Magento server. </summary>
12
+ <description>Description:&#xD;
13
+ &#xD;
14
+ Server Load Monitor (free) features:&#xD;
15
+ &#xD;
16
+ &#x2022; Allows for monitoring the following: CMS, Checkout, Catalog, Catalog Search and other pages; &#xD;
17
+ &#x2022; Divides server performance into four intuitive color-coded zones; &#xD;
18
+ &#x2022; Allows for tracking Average and Max page load and load time for a selected time period;&#xD;
19
+ &#x2022; Displays load statistics in the form of easy and intuitive diagrams in the back-end;&#xD;
20
+ &#x2022; Displays color-coded live server load info in the Admin panel;&#xD;
21
+ &#x2022; Provides statistics for every minute; &#xD;
22
+ &#x2022; Allows for comparing stats for different time periods;&#xD;
23
+ &#x2022; Load statistics are stored in a database;&#xD;
24
+ &#x2022; Compresses data automatically to avoid overloading the database. Default compression settings are optimized and recommended, but Admin is enabled to choose preferred compression settings as well as compress the data manually;&#xD;
25
+ &#x2022; Sends data to AITOC&#x2019;s Server Calculator*; &#xD;
26
+ &#x2022; Comes with a User Manual;&#xD;
27
+ &#x2022; The extension's functionality code is 100% Open Source.&#xD;
28
+ &#xD;
29
+ * Please note that collected data will be sent to AITOC's Server Calculator in order to configure and recommend you better server settings. If you wish to disable sending data for Server Calculator stats, disable the ads, and get alerts about critical load peaks, please get the &lt;a href=&#x201D;http://aitoc.com/en/magentomods_ server_load_monitor_paid.htm&#x201D;&gt;paid version&lt;/a&gt; of the extension. </description>
30
+ <notes>02.29.2013 - Extension release (Compatibility: CE 1.4.&#x445;-1.7.0.2)</notes>
31
+ <authors><author><name>AITOC</name><user>AITOC</user><email>aitoc-connect@aitoc.com</email></author></authors>
32
+ <date>2013-03-29</date>
33
+ <time>15:10:02</time>
34
+ <contents><target name="magelocal"><dir name="Aitoc"><dir name="Aitloadmon"><dir><dir name="Block"><dir name="Adminhtml"><file name="Aitlink.php" hash="4ea78c7a1d6f9e31aead66249a256a1d"/><dir name="Aitloadmon"><file name="BarGraph.php" hash="90e16b55a20987cdd214bc6da435ea6d"/><file name="Filter.php" hash="eade4a66c1a68f1a2334138fd4c33d3a"/><file name="LineGraph.php" hash="34eea0c6add4e36b0a37065cc9d87317"/></dir><file name="Aitloadmon.php" hash="5be3738e8cf84e5b85806701d6efbcd2"/><file name="Calculator.php" hash="4e08727116a1ac204bce97dab899b2e0"/><file name="CalculatorResult.php" hash="4524309b0433fd68ecc828b6a7fc8fc3"/><file name="Indicator.php" hash="9d1e87229cef34dafe0d2ec435f6bb9f"/><file name="Manage.php" hash="dbfdb4f6d788fba1469ab0a61fde9d9d"/></dir></dir><dir name="Helper"><file name="Data.php" hash="5249574ba1c636f4a2e5a59817011fb3"/></dir><dir name="Model"><file name="Aitloadmon.php" hash="8c7e29b4f586640975b8283688ab8bb7"/><file name="Analytics.php" hash="a223097ec56f7eea3329d43fe7e2956b"/><dir name="Calculator"><file name="Abstract.php" hash="2b04ac57b32c874dbb47ffd72292d29a"/><file name="Hardware.php" hash="1164c19b50d86b9a71da74f9629c2e1d"/><file name="Magento.php" hash="1ebcc5312a9e7991439e5ff05d1fb467"/><file name="Software.php" hash="1647fce4800569c1e33d839d9b0d83e8"/></dir><file name="Calculator.php" hash="726d6bc7cf5cd80b4d20cad5475b6da1"/><file name="CompressSource.php" hash="65b4aa585216893143d7ca28bfda6645"/><file name="Dblog.php" hash="4c134db8959a150f8031b71787ae2503"/><file name="LevelSource.php" hash="1937e32cc2b08ffbfc875d3a2f0d362b"/><file name="Manage.php" hash="6b06b0dbbb19d098b1414c6cd4a1bb8a"/><dir name="Mysql4"><dir name="Aitloadmon"><file name="Collection.php" hash="a59a1d250b177fa2175a8a6af21b738d"/></dir><file name="Aitloadmon.php" hash="1f4eddb6272e05d8463a82278cb2a854"/><dir name="Calculator"><file name="Collection.php" hash="15170dd43faeebdfc30e42ebd6270c28"/></dir><file name="Calculator.php" hash="7354931dd66342f5071f782b5de7401e"/><dir name="Dblog"><file name="Collection.php" hash="3dc0dcc5e9d3bb5951573d3d11a8e4dc"/></dir><file name="Dblog.php" hash="4f4e76036d37c170fdf4771f2669dce8"/><file name="UrlRewrite.php" hash="08aecaea96122e801cdb2f9793e22ddf"/></dir><file name="Observer.php" hash="a58aa407da9971ba9cc30afb5be41248"/><file name="Process.php" hash="02b5248392a16268f03fff3f0692dc6d"/><file name="Router.php" hash="9bf8663c8632cf9a410dd67a5393c954"/><file name="SysInfo.php" hash="a5c868f2ec1fa10cbbbca85c917a63f9"/><dir name="System"><file name="Abstract.php" hash="1cdc94c9f08d4507ef57fe766f30fe13"/></dir><file name="System.php" hash="6eeee453abc5efd3c35a24babe613609"/><file name="UrlRewrite.php" hash="ea3849ee4bedc6422b03bdb4be2535fd"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="AitloadmonController.php" hash="0c5f48b6086679ccdb1334d3d9e1e2a4"/><file name="CalculatorController.php" hash="b7f9d6de9974ccb27e37f17ad7cd7515"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="a64ef461fcf678bc7bb28fe5b0e7cc3b"/><file name="config.xml" hash="1f003fc2b2b642537c76b3316ce5ba91"/><file name="system.xml" hash="4fc4ebb203b4a2943067696d7972c77b"/></dir><dir name="sql"><dir name="aitloadmon_setup"><file name="mysql4-install-1.0.0.php" hash="bb6d6c9aec69500be4a10d7944a82dfc"/><file name="mysql4-uninstall-1.0.0.php" hash="177151acef7c5d0c376f1d49bc5ce8c7"/></dir></dir></dir></dir><dir name="Aitsys"><dir><dir name="install"><file name="Aitoc_Aitloadmon.phtml" hash="9a81474e26aac8b23a5b2d2cdd6ab30d"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="aitloadmon.xml" hash="dde1da1952e5309e4125aaa6e328a8fa"/></dir><dir name="template"><dir name="aitloadmon"><file name="back.phtml" hash="ccc9f0ff80649046eddf0faa3b1b50c5"/><file name="bar.phtml" hash="2bd5dd221588543850d3f9a4f4a9e35e"/><file name="graph.phtml" hash="097408b5b89d48df6e80953d0c6a379a"/><file name="indicator.phtml" hash="f0dce9d5c49118b7f98573a91766c563"/><file name="line.phtml" hash="ea7b053633df4f7cdb23423225b8b34b"/><file name="quick.phtml" hash="b0eecb7f8296c293d7abff2cddc5aa5c"/><file name="sysInfo.phtml" hash="674767482f77361975c90c8bc412e4a5"/><file name="system.phtml" hash="b17117164a92d0fda7c99e9cf7027938"/></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Aitoc_Aitloadmon.xml" hash="bee30fb9fd78c808ab4ab7fdfa4c64dd"/></dir></target><target name="magelib"><dir name="Aitoc"><dir name="Aitloadmon"><dir><dir name="Adapter"><file name="Apc.php" hash="c67c74af65e094da996e39f0a8451d62"/><file name="File.php" hash="e440b9dc1c0792680043b8288d22f0a5"/><file name="Memcached.php" hash="b770d4016679b99e9dee9f82e28838a4"/></dir></dir><file name="Collect.php" hash="343f14897e2782418b97f156be2e4797"/></dir></dir></target><target name="mageweb"><dir name="js"><dir name="aitoc"><dir name="aitloadmon"><file name="grafico.bar.custom.js" hash="e54db4c61047afb27a56968a8eec9fc4"/><file name="grafico.line.custom.js" hash="545ece867845d3e46d9091d021013be4"/><file name="grafico.min.js" hash="cde1bec6859b10e4a179aadec3c7e232"/><file name="raphael-min.js" hash="bcbc57198142f86c2e251525b214149d"/></dir></dir></dir></target><target name="mage"><dir name="."><file name="readme.txt" hash="33729d5788f0da4048501c989079b42e"/><file name="License agreement Server Load Monitor.pdf" hash="2c37f69a12f4508aa13181f6cefd264b"/><file name="Server Load Monitor Manual.pdf" hash="21465278b287e1a35d900a92233b796a"/></dir></target></contents>
35
+ <compatible/>
36
+ <dependencies><required><php><min>5.2.0</min><max>5.4.13</max></php></required></dependencies>
37
+ </package>
readme.txt ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @copyright Copyright (c) 2013 AITOC, Inc.
3
+ * @license License agreement could be found at the root folder of this package
4
+ */
5
+ Thank you for choosing AITOC!
6
+
7
+ Please read Installation-Uninstallation Guide.pdf before starting the installation of the module. We also recommend to read the User Manual.
8
+
9
+ To open .pdf format, please download acrobat reader at http://www.adobe.com/products/acrobat/readstep2.html (free of charge).
10
+
11
+
12
+ IMPORTANT!
13
+
14
+ 1. Free version of the extension has no automated installer. The extension downloaded from Magento Connect is already enabled. The extension downloaded from www.aitoc.com needs to be enabled manually in /app/etc/modules/Aitoc_Aitloadmonpaid.xml�
15
+ <modules>
16
+ ��<Aitoc_Aitloadmonpaid>
17
+ �����<active>false</active>�//�set true
18
+ �����<codePool>local</codePool>
19
+ �����<self_name>Server�Load�Monitor�Paid</self_name>
20
+ ��</Aitoc_Aitloadmonpaid>
21
+ </modules>
22
+
23
+ 2. Paid version package includes an automated installer.
24
+
25
+ 3. For both free and paid versions admin should add the following code to the beginning of your index.php to start monitoring:
26
+
27
+ include_once(dirname($_SERVER['SCRIPT_FILENAME']).DIRECTORY_SEPARATOR.'lib'.DIRECTORY_SEPARATOR.'Aitoc'.DIRECTORY_SEPARATOR.'Aitloadmon'.DIRECTORY_SEPARATOR.'Collect.php');new Aitoc_Aitloadmon_Collect();
28
+
29
+
30
+
31
+
32
+ Best Regards,
33
+ AITOC team
34
+ www.aitoc.com