Mageho_Sortproducts - Version 1.0.1

Version Notes

Initial Release

Download this release

Release Info

Developer Ilan PARMENTIER
Extension Mageho_Sortproducts
Version 1.0.1
Comparing to
See all releases


Version 1.0.1

Files changed (51) hide show
  1. app/code/local/Mageho/All/Block/System/Config/Form/Fieldset/Extensions.php +80 -0
  2. app/code/local/Mageho/All/Helper/Data.php +9 -0
  3. app/code/local/Mageho/All/etc/adminhtml.xml +30 -0
  4. app/code/local/Mageho/All/etc/config.xml +61 -0
  5. app/code/local/Mageho/All/etc/system.xml +39 -0
  6. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Info.php +63 -0
  7. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Info.php.LCK +1 -0
  8. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Sortgrid.php +112 -0
  9. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Sortgrid.php.LCK +1 -0
  10. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Tab.php +28 -0
  11. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Tab.php.LCK +1 -0
  12. app/code/local/Mageho/Sortproducts/Helper/Data.php +56 -0
  13. app/code/local/Mageho/Sortproducts/Helper/Data.php.LCK +1 -0
  14. app/code/local/Mageho/Sortproducts/Model/Observer.php +36 -0
  15. app/code/local/Mageho/Sortproducts/Model/Observer.php.LCK +1 -0
  16. app/code/local/Mageho/Sortproducts/Model/Resource/Position.php +43 -0
  17. app/code/local/Mageho/Sortproducts/Model/Resource/Position.php.LCK +1 -0
  18. app/code/local/Mageho/Sortproducts/Model/System/Config/Source/Attributes.php +63 -0
  19. app/code/local/Mageho/Sortproducts/Model/System/Config/Source/Attributes.php.LCK +1 -0
  20. app/code/local/Mageho/Sortproducts/controllers/Adminhtml/Mageho/SortproductsController.php +93 -0
  21. app/code/local/Mageho/Sortproducts/controllers/Adminhtml/Mageho/SortproductsController.php.LCK +1 -0
  22. app/code/local/Mageho/Sortproducts/etc/adminhtml.xml +34 -0
  23. app/code/local/Mageho/Sortproducts/etc/adminhtml.xml.LCK +1 -0
  24. app/code/local/Mageho/Sortproducts/etc/config.xml +112 -0
  25. app/code/local/Mageho/Sortproducts/etc/config.xml.LCK +1 -0
  26. app/code/local/Mageho/Sortproducts/etc/system.xml +70 -0
  27. app/code/local/Mageho/Sortproducts/etc/system.xml.LCK +1 -0
  28. app/design/adminhtml/default/default/layout/mageho/sortproducts.xml +73 -0
  29. app/design/adminhtml/default/default/template/mageho/sortproducts/info.phtml +37 -0
  30. app/design/adminhtml/default/default/template/mageho/sortproducts/sortgrid.phtml +83 -0
  31. app/design/adminhtml/default/default/template/mageho/sortproducts/tab.phtml +19 -0
  32. app/etc/modules/Mageho_All.xml +17 -0
  33. app/etc/modules/Mageho_Sortproducts.xml +21 -0
  34. app/locale/fr_FR/Mageho_All.csv +1 -0
  35. app/locale/fr_FR/Mageho_Sortproducts.csv +14 -0
  36. js/mageho/sortproducts/builder.js +136 -0
  37. js/mageho/sortproducts/controls.js +965 -0
  38. js/mageho/sortproducts/dragdrop.js +974 -0
  39. js/mageho/sortproducts/effects.js +1123 -0
  40. js/mageho/sortproducts/excanvas.js +35 -0
  41. js/mageho/sortproducts/loading.gif +0 -0
  42. js/mageho/sortproducts/opentip.css +460 -0
  43. js/mageho/sortproducts/opentip.js +1285 -0
  44. js/mageho/sortproducts/prototype.js +6081 -0
  45. js/mageho/sortproducts/scriptaculous.js +68 -0
  46. js/mageho/sortproducts/slider.js +275 -0
  47. js/mageho/sortproducts/sound.js +59 -0
  48. js/mageho/sortproducts/unittest.js +568 -0
  49. package.xml +64 -0
  50. skin/adminhtml/default/default/mageho/sortproducts/placeholder.jpg +0 -0
  51. skin/adminhtml/default/default/mageho/sortproducts/sortproducts.css +6 -0
app/code/local/Mageho/All/Block/System/Config/Form/Fieldset/Extensions.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_All
5
+ * @copyright Copyright (c) 2012 - 2013 Mageho (http://www.mageho.com)
6
+ * @license http://www.mageho.com/license Proprietary License
7
+ */
8
+
9
+ class Mageho_All_Block_System_Config_Form_Fieldset_Extensions extends Mage_Adminhtml_Block_System_Config_Form_Fieldset
10
+ {
11
+ protected $_dummyElement;
12
+ protected $_fieldRenderer;
13
+ protected $_values;
14
+
15
+ public function render(Varien_Data_Form_Element_Abstract $element)
16
+ {
17
+ $html = $this->_getHeaderHtml($element);
18
+ $modules = array_keys((array)Mage::getConfig()->getNode('modules')->children());
19
+ sort($modules);
20
+
21
+ foreach ($modules as $moduleName) {
22
+ if (strstr($moduleName,'Mageho_') === false) {
23
+ continue;
24
+ }
25
+ if ($moduleName == 'Mageho_All') {
26
+ continue;
27
+ }
28
+
29
+ $html.= $this->_getFieldHtml($element, $moduleName);
30
+ }
31
+
32
+ $html .= $this->_getFooterHtml($element);
33
+
34
+ return $html;
35
+ }
36
+
37
+ protected function _getDummyElement()
38
+ {
39
+ if (empty($this->_dummyElement)) {
40
+ $this->_dummyElement = new Varien_Object(array('show_in_default' => 1, 'show_in_website' => 1));
41
+ }
42
+ return $this->_dummyElement;
43
+ }
44
+
45
+ protected function _getFieldRenderer()
46
+ {
47
+ if (empty($this->_fieldRenderer)) {
48
+ $this->_fieldRenderer = Mage::getBlockSingleton('adminhtml/system_config_form_field');
49
+ }
50
+ return $this->_fieldRenderer;
51
+ }
52
+
53
+ protected function _getFieldHtml($fieldset, $moduleName)
54
+ {
55
+ $name = strtolower($moduleName);
56
+ $version = Mage::getConfig()->getModuleConfig($moduleName)->version;
57
+
58
+ $field = $fieldset->addField($name, 'label',
59
+ array(
60
+ 'name' => $name,
61
+ 'label' => $moduleName,
62
+ 'value' => $version
63
+ ))->setRenderer($this->_getFieldRenderer());
64
+
65
+ return $field->toHtml();
66
+ }
67
+
68
+ protected function _convertVersion($v)
69
+ {
70
+ $digits = @explode('.', $v);
71
+ $version = 0;
72
+
73
+ if (is_array($digits)) {
74
+ foreach($digits as $k=>$v) {
75
+ $version += ($v * pow(10, max(0, (3-$k))));
76
+ }
77
+ }
78
+ return $version;
79
+ }
80
+ }
app/code/local/Mageho/All/Helper/Data.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_All
5
+ * @copyright Copyright (c) 2012 - 2013 Mageho (http://www.mageho.com)
6
+ * @license http://www.mageho.com/license Proprietary License
7
+ */
8
+
9
+ class Mageho_All_Helper_Data extends Mage_Core_Helper_Abstract {}
app/code/local/Mageho/All/etc/adminhtml.xml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_All
6
+ * @copyright Copyright (c) 2012 - 2013 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+ -->
10
+ <config>
11
+ <acl>
12
+ <resources>
13
+ <admin>
14
+ <children>
15
+ <system>
16
+ <children>
17
+ <config>
18
+ <children>
19
+ <magehoall>
20
+ <title>Mageho</title>
21
+ </magehoall>
22
+ </children>
23
+ </config>
24
+ </children>
25
+ </system>
26
+ </children>
27
+ </admin>
28
+ </resources>
29
+ </acl>
30
+ </config>
app/code/local/Mageho/All/etc/config.xml ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_All
6
+ * @copyright Copyright (c) 2012 - 2013 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+ -->
10
+ <config>
11
+ <modules>
12
+ <Mageho_All>
13
+ <version>1.0.0</version>
14
+ </Mageho_All>
15
+ </modules>
16
+
17
+ <global>
18
+ <blocks>
19
+ <magehoall><class>Mageho_All_Block</class></magehoall>
20
+ </blocks>
21
+
22
+ <models>
23
+ <magehoall><class>Mageho_All_Model</class></magehoall>
24
+ </models>
25
+
26
+ <helpers>
27
+ <magehoall><class>Mageho_All_Helper</class></magehoall>
28
+ </helpers>
29
+ </global>
30
+
31
+ <adminhtml>
32
+ <acl>
33
+ <resources>
34
+ <admin>
35
+ <children>
36
+ <system>
37
+ <children>
38
+ <config>
39
+ <children>
40
+ <magehoall>
41
+ <title>Mageho</title>
42
+ </magehoall>
43
+ </children>
44
+ </config>
45
+ </children>
46
+ </system>
47
+ </children>
48
+ </admin>
49
+ </resources>
50
+ </acl>
51
+ <translate>
52
+ <modules>
53
+ <Mageho_All>
54
+ <files>
55
+ <default>Mageho_All.csv</default>
56
+ </files>
57
+ </Mageho_All>
58
+ </modules>
59
+ </translate>
60
+ </adminhtml>
61
+ </config>
app/code/local/Mageho/All/etc/system.xml ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_All
6
+ * @copyright Copyright (c) 2012 - 2013 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+ -->
10
+ <config>
11
+ <tabs>
12
+ <mageho translate="label" module="magehoall">
13
+ <label>Mageho</label>
14
+ <sort_order>300</sort_order>
15
+ </mageho>
16
+ </tabs>
17
+ <sections>
18
+ <magehoall translate="label" module="magehoall">
19
+ <label>Info</label>
20
+ <tab>mageho</tab>
21
+ <frontend_type>text</frontend_type>
22
+ <sort_order>0</sort_order>
23
+ <show_in_default>1</show_in_default>
24
+ <show_in_website>1</show_in_website>
25
+ <show_in_store>1</show_in_store>
26
+ <groups>
27
+ <extensions translate="label">
28
+ <label>Installed Mageho Extensions</label>
29
+ <frontend_type>text</frontend_type>
30
+ <frontend_model>magehoall/system_config_form_fieldset_extensions</frontend_model>
31
+ <sort_order>0</sort_order>
32
+ <show_in_default>1</show_in_default>
33
+ <show_in_website>1</show_in_website>
34
+ <show_in_store>1</show_in_store>
35
+ </extensions>
36
+ </groups>
37
+ </magehoall>
38
+ </sections>
39
+ </config>
app/code/local/Mageho/Sortproducts/Block/Adminhtml/Info.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @version 1.0.0
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+
10
+ class Mageho_Sortproducts_Block_Adminhtml_Info extends Mage_Adminhtml_Block_Template
11
+ {
12
+ public function __construct()
13
+ {
14
+ parent::__construct();
15
+ $this->setTemplate('mageho/sortproducts/info.phtml');
16
+ }
17
+
18
+ protected function _prepareLayout()
19
+ {
20
+ parent::_prepareLayout();
21
+ return $this;
22
+ }
23
+
24
+ public function getSavePercentage($product)
25
+ {
26
+ if ($product->getPrice() == $product->getFinalPrice()) {
27
+ return false;
28
+ }
29
+
30
+ $save = number_format( (100 - ( $product->getFinalPrice() / $product->getPrice() ) * 100) );
31
+ return sprintf('-%d%%', $save);
32
+ }
33
+
34
+ public function getAttributesToShow()
35
+ {
36
+ $columnSettings = array();
37
+ $attributes = explode(',', Mage::getStoreConfig('mageho_sortproducts/general/show_attributes'));
38
+ foreach ($attributes as $attribute) {
39
+ $columnSettings[] = trim($attribute);
40
+ }
41
+
42
+ if (empty($columnSettings)) {
43
+ return false;
44
+ }
45
+
46
+ $countColumnSettings = count($columnSettings);
47
+
48
+ if ($countColumnSettings < 1) {
49
+ return false;
50
+ }
51
+
52
+ if ($countColumnSettings == 1 && current($columnSettings) == '') {
53
+ return false;
54
+ }
55
+
56
+ return $columnSettings;
57
+ }
58
+
59
+ public function getAttributeFrontendLabel($product, $attribute)
60
+ {
61
+ return $product->getResource()->getAttribute($attribute)->getFrontendLabel();
62
+ }
63
+ }
app/code/local/Mageho/Sortproducts/Block/Adminhtml/Info.php.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/Block/Adminhtml/Sortgrid.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @version 1.0.0
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+
10
+ class Mageho_Sortproducts_Block_Adminhtml_Sortgrid extends Mage_Adminhtml_Block_Template
11
+ {
12
+ public function __construct()
13
+ {
14
+ parent::__construct();
15
+ $this->setTemplate('mageho/sortproducts/sortgrid.phtml');
16
+ }
17
+
18
+ protected function _prepareLayout()
19
+ {
20
+ parent::_prepareLayout();
21
+ return $this;
22
+ }
23
+
24
+ /**
25
+ * ACL validation before html generation
26
+ *
27
+ * @return string
28
+ */
29
+ protected function _toHtml()
30
+ {
31
+ if (Mage::getSingleton('admin/session')->isAllowed('catalog/categories')) {
32
+ return parent::_toHtml();
33
+ }
34
+ return '';
35
+ }
36
+
37
+ public function getProductsCollection()
38
+ {
39
+ if (!$this->_productsCollection)
40
+ {
41
+ $store = (int) $this->getRequest()->getParam('store', 0);
42
+ $category_id = (int) $this->getRequest()->getParam('id', 0);
43
+
44
+ $category = Mage::getSingleton('catalog/category')->setStoreId($store)->load($category_id);
45
+ $products = $category->getProductsPosition();
46
+
47
+ $collection = Mage::getModel('catalog/product')->getCollection()
48
+ #->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes())
49
+ ->addAttributeToSelect('*')
50
+ ->joinField('position', 'catalog/category_product', 'position', 'product_id=entity_id', '{{table}}.category_id='.$category_id, 'left')
51
+ #->joinField('qty', 'cataloginventory/stock_item', 'qty', 'product_id=entity_id', '{{table}}.stock_id=1', 'left')
52
+ #->joinField('rating', 'review/review_aggregate', 'rating_summary', 'entity_pk_value=entity_id', '{{table}}.store_id=1', 'left') // Modifier store_id
53
+ ->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner', $store)
54
+ ->addFieldToFilter('visibility', array('in' => array(Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_CATALOG, Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH)))
55
+ ->addFieldToFilter('entity_id', array('in' => array_keys($products)))
56
+ ->addStoreFilter($store);
57
+
58
+ if (Mage::helper('mageho_sortproducts')->displayOnlyEnabledProducts()) {
59
+ $collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner', $store)
60
+ ->addFieldToFilter('status', 1);
61
+ }
62
+
63
+ // On cache les produits épuisés
64
+ if (Mage::helper('mageho_sortproducts')->displayOutStockProducts()) {
65
+ if ($websiteId = Mage::app()->getWebsite()->getWebsiteId()) {
66
+ $collection->joinField('stock_status', 'cataloginventory/stock_status', 'stock_status', 'product_id=entity_id', array('stock_status' => Mage_CatalogInventory_Model_Stock_Status::STATUS_IN_STOCK, 'website_id' => $websiteId));
67
+ }
68
+ }
69
+
70
+ $collection->setOrder('position', 'asc');
71
+
72
+ if ($limit = Mage::helper('mageho_sortproducts')->limitSortProducts()) {
73
+ $collection->setPageSize($limit);
74
+ }
75
+
76
+ $collection->setCurPage(1);
77
+
78
+ $this->_productsCollection = $collection;
79
+ }
80
+ return $this->_productsCollection;
81
+ }
82
+
83
+ public function getImageWidth()
84
+ {
85
+ return Mage::helper('mageho_sortproducts')->getImageWidth();
86
+ }
87
+
88
+ /**
89
+ * Retrieve Session Form Key
90
+ *
91
+ * @return string
92
+ */
93
+ public function getFormKey()
94
+ {
95
+ return Mage::getSingleton('core/session')->getFormKey();
96
+ }
97
+
98
+ public function getSaveUrl()
99
+ {
100
+ return $this->getUrl('*/*/save', array('_current' => true));
101
+ }
102
+
103
+ public function getProductInfoUrl($product)
104
+ {
105
+ return $this->getUrl('*/*/info', array('id' => $product->getId(), '_current' => true));
106
+ }
107
+
108
+ public function isDisabled($product)
109
+ {
110
+ return $product->getStatus() && ($product->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_DISABLED);
111
+ }
112
+ }
app/code/local/Mageho/Sortproducts/Block/Adminhtml/Sortgrid.php.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/Block/Adminhtml/Tab.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @version 1.0.0
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+
10
+ class Mageho_Sortproducts_Block_Adminhtml_Tab extends Mage_Adminhtml_Block_Template
11
+ {
12
+ public function __construct()
13
+ {
14
+ parent::__construct();
15
+ $this->setTemplate('mageho/sortproducts/tab.phtml');
16
+ }
17
+
18
+ protected function _prepareLayout()
19
+ {
20
+ parent::_prepareLayout();
21
+ return $this;
22
+ }
23
+
24
+ public function getSortProductsUrl()
25
+ {
26
+ return $this->getUrl('adminhtml/mageho_sortproducts/index', array('_current' => true));
27
+ }
28
+ }
app/code/local/Mageho/Sortproducts/Block/Adminhtml/Tab.php.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/Helper/Data.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @version 1.0.0
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+
10
+ class Mageho_Sortproducts_Helper_Data extends Mage_Core_Helper_Abstract
11
+ {
12
+ const SORTPRODUCTS_DISPLAY_OUT_STOCK_PRODUCTS = 'sortproducts/general/display_out_stock_products';
13
+ const SORTPRODUCTS_DISPLAY_ONLY_ENABLED_PRODUCTS = 'sortproducts/general/display_only_enabled_products';
14
+ const SORTPRODUCTS_LIMIT_PRODUCTS = 'sortproducts/general/limit_products';
15
+
16
+ /**
17
+ * Translate
18
+ *
19
+ * @return string
20
+ */
21
+ public function __()
22
+ {
23
+ $args = func_get_args();
24
+ $expr = new Mage_Core_Model_Translate_Expr(array_shift($args), 'Mageho_Sortproducts');
25
+ array_unshift($args, $expr);
26
+ return utf8_encode(Mage::app()->getTranslator()->translate($args));
27
+ }
28
+
29
+ public function displayOutStockProducts()
30
+ {
31
+ return Mage::getStoreConfigFlag(self::SORTPRODUCTS_DISPLAY_OUT_STOCK_PRODUCTS);
32
+ }
33
+
34
+ public function displayOnlyEnabledProducts()
35
+ {
36
+ return Mage::getStoreConfigFlag(self::SORTPRODUCTS_DISPLAY_ONLY_ENABLED_PRODUCTS);
37
+ }
38
+
39
+ public function limitSortProducts()
40
+ {
41
+ $limit = (int) Mage::getStoreConfig(self::SORTPRODUCTS_LIMIT_PRODUCTS, 0);
42
+ if ($limit > 0) {
43
+ return $limit;
44
+ }
45
+ }
46
+
47
+ public function getImageWidth()
48
+ {
49
+ $width = (int) Mage::getStoreConfig('mageho_sortproducts/general/image_width');
50
+ if ($width > 0) {
51
+ return $width;
52
+ } else {
53
+ return 60;
54
+ }
55
+ }
56
+ }
app/code/local/Mageho/Sortproducts/Helper/Data.php.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/Model/Observer.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @version 1.0.0
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+
10
+ class Mageho_Sortproducts_Model_Observer
11
+ {
12
+ public function addTabToCategoryPage($observer)
13
+ {
14
+ $event = $observer->getEvent();
15
+ $tabs = $event->getTabs();
16
+ $category = $tabs->getCategory();
17
+ $count = $category->getProductCount();
18
+
19
+ if ($category->getId() && ($count > 1))
20
+ {
21
+ $tabs->addTab('mageho_sortproducts', array(
22
+ 'label' => Mage::helper('mageho_sortproducts')->__('Sort Category Products'),
23
+ 'content' => $tabs->getLayout()->createBlock('mageho_sortproducts/adminhtml_tab')->setTemplate('mageho/sortproducts/tab.phtml')->toHtml()
24
+
25
+ /*
26
+ * Pour charger le bloc en ajax ajouter ces lignes et enlever la clé content juste au-dessus
27
+ */
28
+
29
+ #'url' => Mage::getUrl('adminhtml/mageho_sortproducts/tab', array('_current' => true)),
30
+ #'class' => 'ajax'
31
+ ));
32
+ }
33
+
34
+ return $this;
35
+ }
36
+ }
app/code/local/Mageho/Sortproducts/Model/Observer.php.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/Model/Resource/Position.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproduct
5
+ * @version 1.0.0
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+
10
+ #class Mageho_Sortproducts_Model_Mysql4_Position extends Mage_Catalog_Model_Resource_Eav_Mysql4_Category
11
+ class Mageho_Sortproducts_Model_Resource_Position extends Mage_Catalog_Model_Resource_Category
12
+ {
13
+ /**
14
+ * Initialize resource
15
+ */
16
+ public function __construct()
17
+ {
18
+ parent::__construct();
19
+ $resource = Mage::getSingleton('core/resource');
20
+ $this->setType('catalog_category')
21
+ ->setConnection(
22
+ $resource->getConnection('catalog_read'),
23
+ $resource->getConnection('catalog_write')
24
+ );
25
+
26
+ $this->_productCategoryTable = $resource->getTableName('catalog/category_product');
27
+ $this->_productCategoryIndexTable = $resource->getTableName('catalog/category_product_index');
28
+ }
29
+
30
+ public function save($position, $productId, $categoryId)
31
+ {
32
+ $condition = array(
33
+ $this->_getWriteAdapter()->quoteInto("product_id=?", $productId),
34
+ $this->_getWriteAdapter()->quoteInto("category_id=?", $categoryId)
35
+ );
36
+
37
+ $db = $this->_getWriteAdapter();
38
+ $db->update($this->_productCategoryTable, array('position' => $position), $condition);
39
+ $db->update($this->_productCategoryIndexTable, array('position' => $position), $condition);
40
+
41
+ return $this;
42
+ }
43
+ }
app/code/local/Mageho/Sortproducts/Model/Resource/Position.php.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/Model/System/Config/Source/Attributes.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @version 1.0.0
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+
10
+ class Mageho_Sortproducts_Model_System_Config_Source_Attributes
11
+ {
12
+ public $doNotDisplayAttributesArray = array('price', 'tier_price', 'group_price', 'special_price', 'name', 'sku',
13
+ 'description', 'tax_class_id', 'url_key', 'options_container', 'price_view', 'gift_message_available',
14
+ 'custom_design', 'custom_design_from', 'custom_design_to', 'custom_layout_update', 'page_layout',
15
+ 'visibility', 'status', 'enable_googlecheckout');
16
+
17
+ public function toOptionArray()
18
+ {
19
+ $cols = array();
20
+ $cols[] = array('value' => '', 'label' => Mage::helper('mageho_sortproducts')->__('None'));
21
+ $cols[] = array('value' => 'type_id', 'label' => Mage::helper('mageho_sortproducts')->__('Product Type (simple, bundle, etc)'));
22
+ $cols[] = array('value' => 'attribute_set_id', 'label' => Mage::helper('mageho_sortproducts')->__('Attribute Set'));
23
+ $cols[] = array('value' => 'created_at', 'label' => Mage::helper('mageho_sortproducts')->__('Date Created'));
24
+
25
+ foreach ($this->getAttributes() as $attribute) {
26
+ $label = $attribute->getFrontendLabel() . ' - ' . $attribute->getAttributeCode() . ' #'.$attribute->getAttributeId();
27
+
28
+ $cols[] = array('value' => $attribute->getAttributeCode(), 'label' => $label);
29
+ }
30
+
31
+ return $cols;
32
+ }
33
+
34
+ /**
35
+ * @return Mage_Eav_Model_Mysql4_Entity_Attribute_Collection
36
+ */
37
+ protected function getAttributes()
38
+ {
39
+ if (version_compare(Mage::getVersion(), '1.4', '>=')) {
40
+ $collection = Mage::getResourceModel( 'catalog/product_attribute_collection');
41
+ } else {
42
+ $type_id = Mage::getModel('eav/entity')->setType('catalog_product')->getTypeId();
43
+ $collection = Mage::getResourceModel('eav/entity_attribute_collection');
44
+ $collection->setEntityTypeFilter($type_id);
45
+ }
46
+
47
+ $collection->addFilter('is_visible', 1);
48
+
49
+ $allowedAttributes = array();
50
+ foreach ($collection->getItems() as $attribute) {
51
+ if ( in_array($attribute->getFrontendInput(), array('media_image', 'gallery')) ) {
52
+ continue;
53
+ }
54
+ if ( in_array($attribute->getAttributeCode(), $this->doNotDisplayAttributesArray) ) {
55
+ continue;
56
+ }
57
+
58
+ $allowedAttributes[] = $attribute;
59
+ }
60
+
61
+ return $allowedAttributes;
62
+ }
63
+ }
app/code/local/Mageho/Sortproducts/Model/System/Config/Source/Attributes.php.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/controllers/Adminhtml/Mageho/SortproductsController.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @version 1.0.0
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+
10
+ class Mageho_Sortproducts_Adminhtml_Mageho_SortproductsController
11
+ extends Mage_Adminhtml_Controller_Action
12
+ {
13
+ public function indexAction()
14
+ {
15
+ $this->loadLayout();
16
+ $this->renderLayout();
17
+ }
18
+
19
+ public function tabAction()
20
+ {
21
+ $this->getResponse()->setBody(
22
+ $this->getLayout()->createBlock('mageho_sortproducts/adminhtml_tab')->toHtml()
23
+ );
24
+ }
25
+
26
+ public function infoAction()
27
+ {
28
+ $productId = $this->getRequest()->getParam('id', false);
29
+ $product = Mage::getModel('catalog/product')
30
+ ->setStoreId($this->getRequest()->getParam('store', 0));
31
+
32
+ if ($productId) {
33
+ $product->load($productId);
34
+
35
+ Mage::register('product', $product);
36
+ Mage::register('current_product', $product);
37
+
38
+ $this->getResponse()->setBody(
39
+ $this->getLayout()->createBlock('mageho_sortproducts/adminhtml_info')->toHtml()
40
+ );
41
+ }
42
+ }
43
+
44
+ public function saveAction()
45
+ {
46
+ $data = $this->getRequest()->getPost('data');
47
+ $categoryId = $this->getRequest()->getParam('id', false);
48
+
49
+ try {
50
+ if (! $data) {
51
+ throw new Exception('no data post parameters');
52
+ }
53
+
54
+ $category = Mage::getModel('catalog/category')
55
+ ->setStoreId($this->getRequest()->getParam('store', 0));
56
+
57
+ if ($categoryId) {
58
+ $category->load($categoryId);
59
+
60
+ if ($category->getId()) {
61
+ parse_str($data);
62
+ for ($i = 0; $i < count($sortlist); $i++) {
63
+ $position = $i;
64
+ $productId = $sortlist[$i];
65
+
66
+ Mage::getResourceModel('mageho_sortproducts/position')->save($position, $productId, $category->getId());
67
+ }
68
+
69
+ Mage::app()->cleanCache(array(Mage_Catalog_Model_Category::CACHE_TAG.'_'.$category->getId()));
70
+
71
+ Mage::getSingleton('adminhtml/session')->addSuccess(
72
+ Mage::helper('mageho_sortproducts')->__('The position of products has been saved with success.')
73
+ );
74
+ }
75
+ }
76
+ } catch (Exception $e) {
77
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
78
+ }
79
+
80
+ $resetUrl = Mage::helper('adminhtml')->getUrl('adminhtml/catalog_category/edit', array('active_tab_id' => 'category_info_tabs_sortproducts', '_current' => true));
81
+ $this->getResponse()->setBody("window.top.categoryReset('{$resetUrl}', true);");
82
+ }
83
+
84
+ /**
85
+ * Check if admin has permissions to visit categories pages
86
+ *
87
+ * @return boolean
88
+ */
89
+ protected function _isAllowed()
90
+ {
91
+ return Mage::getSingleton('admin/session')->isAllowed('catalog/categories');
92
+ }
93
+ }
app/code/local/Mageho/Sortproducts/controllers/Adminhtml/Mageho/SortproductsController.php.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/etc/adminhtml.xml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_SortProducts
6
+ * @version 1.0.0
7
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
8
+ * @license http://www.mageho.com/license Proprietary License
9
+ */
10
+ -->
11
+ <config>
12
+ <acl>
13
+ <resources>
14
+ <all translate="title" module="mageho_sortproducts">
15
+ <title>Allow Everything</title>
16
+ </all>
17
+ <admin>
18
+ <children>
19
+ <system>
20
+ <children>
21
+ <config>
22
+ <children>
23
+ <mageho_sortproducts translate="title" module="mageho_sortproducts">
24
+ <title>Merchandising</title>
25
+ </mageho_sortproducts>
26
+ </children>
27
+ </config>
28
+ </children>
29
+ </system>
30
+ </children>
31
+ </admin>
32
+ </resources>
33
+ </acl>
34
+ </config>
app/code/local/Mageho/Sortproducts/etc/adminhtml.xml.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/etc/config.xml ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_Sortproducts
6
+ * @version 1.0.0
7
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
8
+ * @license http://www.mageho.com/license Proprietary License
9
+ */
10
+ -->
11
+ <config>
12
+ <modules>
13
+ <Mageho_Sortproducts>
14
+ <version>1.0.0</version>
15
+ </Mageho_Sortproducts>
16
+ </modules>
17
+ <global>
18
+ <blocks>
19
+ <mageho_sortproducts>
20
+ <class>Mageho_Sortproducts_Block</class>
21
+ </mageho_sortproducts>
22
+ </blocks>
23
+ <helpers>
24
+ <mageho_sortproducts>
25
+ <class>Mageho_Sortproducts_Helper</class>
26
+ </mageho_sortproducts>
27
+ </helpers>
28
+ <models>
29
+ <mageho_sortproducts>
30
+ <class>Mageho_Sortproducts_Model</class>
31
+ <!-- resourceModel>sortproducts_mysql4</resourceModel -->
32
+ <resourceModel>mageho_sortproducts_resource</resourceModel>
33
+ </mageho_sortproducts>
34
+ <!--sortproducts_mysql4>
35
+ <class>Mageho_Sortproducts_Model_Mysql4</class>
36
+ </sortproducts_mysql4-->
37
+ <mageho_sortproducts_resource>
38
+ <class>Mageho_Sortproducts_Model_Resource</class>
39
+ </mageho_sortproducts_resource>
40
+ </models>
41
+ <resources>
42
+ <mageho_sortproducts_setup>
43
+ <setup>
44
+ <module>Mageho_Sortproducts</module>
45
+ </setup>
46
+ <connection>
47
+ <use>core_setup</use>
48
+ </connection>
49
+ </mageho_sortproducts_setup>
50
+ <mageho_sortproducts_write>
51
+ <connection>
52
+ <use>core_write</use>
53
+ </connection>
54
+ </mageho_sortproducts_write>
55
+ <mageho_sortproducts_read>
56
+ <connection>
57
+ <use>core_read</use>
58
+ </connection>
59
+ </mageho_sortproducts_read>
60
+ </resources>
61
+ <events>
62
+ <adminhtml_catalog_category_tabs>
63
+ <observers>
64
+ <mageho_sortproducts>
65
+ <type>singleton</type>
66
+ <class>mageho_sortproducts/observer</class>
67
+ <method>addTabToCategoryPage</method>
68
+ </mageho_sortproducts>
69
+ </observers>
70
+ </adminhtml_catalog_category_tabs>
71
+ </events>
72
+ </global>
73
+ <admin>
74
+ <routers>
75
+ <adminhtml>
76
+ <args>
77
+ <modules>
78
+ <Mageho_Sortproducts before="Mage_Adminhtml">Mageho_Sortproducts_Adminhtml</Mageho_Sortproducts>
79
+ </modules>
80
+ </args>
81
+ </adminhtml>
82
+ </routers>
83
+ </admin>
84
+ <adminhtml>
85
+ <translate>
86
+ <modules>
87
+ <Mageho_Sortproducts>
88
+ <files>
89
+ <default>Mageho_Sortproducts.csv</default>
90
+ </files>
91
+ </Mageho_Sortproducts>
92
+ </modules>
93
+ </translate>
94
+ <layout>
95
+ <updates>
96
+ <mageho_sortproducts>
97
+ <file>mageho/sortproducts.xml</file>
98
+ </mageho_sortproducts>
99
+ </updates>
100
+ </layout>
101
+ </adminhtml>
102
+ <default>
103
+ <mageho_sortproducts>
104
+ <general>
105
+ <limit_products>0</limit_products>
106
+ <image_width>50</image_width>
107
+ <display_only_enabled_products>0</display_only_enabled_products>
108
+ <display_out_stock_products>0</display_out_stock_products>
109
+ </general>
110
+ </mageho_sortproducts>
111
+ </default>
112
+ </config>
app/code/local/Mageho/Sortproducts/etc/config.xml.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/code/local/Mageho/Sortproducts/etc/system.xml ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_Sortproducts
6
+ * @version 1.0.0
7
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
8
+ * @license http://www.mageho.com/license Proprietary License
9
+ */
10
+ -->
11
+ <config>
12
+ <sections>
13
+ <mageho_sortproducts translate="label" module="mageho_sortproducts">
14
+ <label>Merchandising</label>
15
+ <tab>mageho</tab>
16
+ <frontend_type>text</frontend_type>
17
+ <sort_order>200</sort_order>
18
+ <show_in_default>1</show_in_default>
19
+ <show_in_website>1</show_in_website>
20
+ <show_in_store>0</show_in_store>
21
+ <groups>
22
+ <general translate="label">
23
+ <label>General</label>
24
+ <show_in_default>1</show_in_default>
25
+ <show_in_website>0</show_in_website>
26
+ <show_in_store>0</show_in_store>
27
+ <fields>
28
+ <limit_products translate="label comment">
29
+ <label>Limit number of products in iframe</label>
30
+ <comment>Enter 0 for no limit - This parameter can improve performance loading</comment>
31
+ <frontend_type>text</frontend_type>
32
+ <sort_order>0</sort_order>
33
+ <show_in_default>1</show_in_default>
34
+ <show_in_website>1</show_in_website>
35
+ </limit_products>
36
+ <image_width translate="label">
37
+ <label>Image Width</label>
38
+ <frontend_type>text</frontend_type>
39
+ <sort_order>10</sort_order>
40
+ <show_in_default>1</show_in_default>
41
+ <show_in_website>1</show_in_website>
42
+ </image_width>
43
+ <show_attributes translate="label">
44
+ <label>Show Attributes</label>
45
+ <frontend_type>multiselect</frontend_type>
46
+ <source_model>Mageho_Sortproducts_Model_System_Config_Source_Attributes</source_model>
47
+ <sort_order>20</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>1</show_in_website>
50
+ </show_attributes>
51
+ <display_only_enabled_products translate="label">
52
+ <label>Display Only Enabled Products</label>
53
+ <frontend_type>select</frontend_type>
54
+ <sort_order>30</sort_order>
55
+ <show_in_default>1</show_in_default>
56
+ <source_model>adminhtml/system_config_source_yesno</source_model>
57
+ </display_only_enabled_products>
58
+ <display_out_stock_products translate="label">
59
+ <label>Display Out of Stock Products</label>
60
+ <frontend_type>select</frontend_type>
61
+ <sort_order>40</sort_order>
62
+ <show_in_default>1</show_in_default>
63
+ <source_model>adminhtml/system_config_source_yesno</source_model>
64
+ </display_out_stock_products>
65
+ </fields>
66
+ </general>
67
+ </groups>
68
+ </mageho_sortproducts>
69
+ </sections>
70
+ </config>
app/code/local/Mageho/Sortproducts/etc/system.xml.LCK ADDED
@@ -0,0 +1 @@
 
1
+ Ilan Parmentier||ilan.parmentier@artbambou.com
app/design/adminhtml/default/default/layout/mageho/sortproducts.xml ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_Sortproducts
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ * @version 1.0.0
9
+ */
10
+ -->
11
+ <layout version="0.1.0">
12
+ <adminhtml_mageho_sortproducts_index>
13
+ <update handle="popup" />
14
+ <remove name="head.calendar" />
15
+ <remove name="notifications" />
16
+ <remove name="footer" />
17
+
18
+ <reference name="head">
19
+ <action method="removeItem"><type>js</type><name>prototype/prototype.js</name></action>
20
+ <action method="removeItem"><type>js</type><name>prototype/window.js</name></action>
21
+ <action method="removeItem"><type>js</type><name>scriptaculous/builder.js</name></action>
22
+ <action method="removeItem"><type>js</type><name>scriptaculous/effects.js</name></action>
23
+ <action method="removeItem"><type>js</type><name>scriptaculous/dragdrop.js</name></action>
24
+ <action method="removeItem"><type>js</type><name>scriptaculous/controls.js</name></action>
25
+ <action method="removeItem"><type>js</type><name>scriptaculous/slider.js</name></action>
26
+ <action method="removeItem"><type>js</type><name>lib/ccard.js</name></action>
27
+ <action method="removeItem"><type>js</type><name>prototype/validation.js</name></action>
28
+ <action method="removeItem"><type>js</type><name>varien/js.js</name></action>
29
+ <action method="removeItem"><type>js</type><name>mage/translate.js</name></action>
30
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/hash.js</name></action>
31
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/events.js</name></action>
32
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/loader.js</name></action>
33
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/grid.js</name></action>
34
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/tabs.js</name></action>
35
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/form.js</name></action>
36
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/accordion.js</name></action>
37
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/tools.js</name></action>
38
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/uploader.js</name></action>
39
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/product.js</name></action>
40
+ <action method="removeItem"><type>js</type><name>calendar/calendar.js</name></action>
41
+ <action method="removeItem"><type>js</type><name>calendar/calendar-setup.js</name></action>
42
+ <action method="removeItem"><type>js</type><name>mage/adminhtml/rules.js</name></action>
43
+
44
+ <!-- Extension EmProductsFilter -->
45
+ <action method="removeItem"><type>js</type><name>em/productsfilter/productsfilter.js</name></action>
46
+
47
+ <!-- Extension Files Download -->
48
+ <action method="removeItem"><type>js</type><name>mageworx/fileuploader.js</name></action>
49
+
50
+ <!-- Extension EnhancedGrid -->
51
+ <action method="removeItem"><type>js</type><name>tbt/enhancedgrid/enhancedgrid.js</name></action>
52
+ <action method="removeItem"><type>js</type><name>tbt/enhancedgrid/egsupplemental.js</name></action>
53
+ <action method="removeItem"><type>js</type><name>tbt/enhancedgrid/customfunctions/catalog_products.js</name></action>
54
+
55
+ <action method="addJs"><script>mageho/sortproducts/prototype.js</script></action>
56
+
57
+ <!-- Ne fonctionne que si les fichiers javascript ne sont pas fusionnés -->
58
+ <!-- action method="addJs"><script>mageho/sortproducts/scriptaculous.js</script></action -->
59
+
60
+ <action method="addJs"><script>mageho/sortproducts/effects.js</script></action>
61
+ <action method="addJs"><script>mageho/sortproducts/dragdrop.js</script></action>
62
+ <action method="addJs"><script>mageho/sortproducts/opentip.js</script></action>
63
+ <action method="addJs"><script>mageho/sortproducts/excanvas.js</script></action>
64
+ <action method="addItem"><type>js_css</type><name>mageho/sortproducts/opentip.css</name></action>
65
+
66
+ <action method="addCss"><name>mageho/sortproducts/sortproducts.css</name></action>
67
+ </reference>
68
+
69
+ <reference name="content">
70
+ <block type="mageho_sortproducts/adminhtml_sortgrid" name="mageho.sortgrid" />
71
+ </reference>
72
+ </adminhtml_mageho_sortproducts_index>
73
+ </layout>
app/design/adminhtml/default/default/template/mageho/sortproducts/info.phtml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
6
+ * @license http://www.mageho.com/license Proprietary License
7
+ * @version 1.0.0
8
+ */
9
+ ?>
10
+ <?php $_product = Mage::registry('current_product'); ?>
11
+ <div>
12
+ <ul class="square-list">
13
+ <li><small><?php echo $this->__('ID'); ?> : <?php echo $_product->getId(); ?></small></li>
14
+
15
+ <?php if ($_product->getSku()): ?>
16
+ <li><small><?php echo $this->__('SKU'); ?> : <?php echo $_product->getSku(); ?></small></li>
17
+ <?php endif; ?>
18
+
19
+ <li>
20
+ <?php echo $this->__('Price'); ?> :
21
+ <?php if ($_product->getSpecialPrice()): ?>
22
+ <del><?php echo $this->helper('core')->currency($_product->getPrice()); ?></del>
23
+ <ins><?php echo $this->helper('core')->currency($_product->getSpecialPrice()); ?></ins>
24
+
25
+ <?php echo $this->getSavePercentage($_product); ?>
26
+ <?php else: ?>
27
+ <?php echo $this->helper('core')->currency($_product->getPrice()); ?>
28
+ <?php endif; ?>
29
+ </li>
30
+
31
+ <?php if ($attributes = $this->getAttributesToShow()): ?>
32
+ <?php foreach($attributes as $attribute): ?>
33
+ <li><?php echo $this->getAttributeFrontendLabel($_product, $attribute); ?> : <?php echo ($_product->getData($attribute) ? $_product->getData($attribute) : $this->__('N.C.')); ?></li>
34
+ <?php endforeach; ?>
35
+ <?php endif; ?>
36
+ </ul>
37
+ </div>
app/design/adminhtml/default/default/template/mageho/sortproducts/sortgrid.phtml ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
6
+ * @license http://www.mageho.com/license Proprietary License
7
+ * @version 1.0.0
8
+ */
9
+ ?>
10
+ <?php if (($_products = $this->getProductsCollection()) && $_products->getSize()): ?>
11
+ <script type="text/javascript">
12
+ //<![CDATA[
13
+ document.observe('dom:loaded', function() {
14
+ Sortable.create('sortlist', {
15
+ tag: 'li',
16
+ only: 'sorting',
17
+ hoverclass: 'over',
18
+ overlap: 'horizontal',
19
+ ghosting: false,
20
+ constraint: false,
21
+ scroll: window,
22
+ scrollSensitivity: 30,
23
+ scrollSpeed: 25
24
+ })
25
+
26
+ $$('button.button-save-sortable').each(function(el) {
27
+ el.onclick = function() {
28
+ new Ajax.Request("<?php echo $this->getSaveUrl(); ?>", {
29
+ method: "post",
30
+ evalScripts: true,
31
+ onLoading: function() { parent.$('loading-mask').show() },
32
+ onSuccess: function(transport) { parent.$('loading-mask').hide() },
33
+ onComplete:function(transport) { eval(transport.responseText) },
34
+ parameters: {
35
+ data: Sortable.serialize('sortlist'),
36
+ form_key: '<?php echo $this->getFormKey(); ?>'
37
+ }
38
+ })
39
+ }
40
+ })
41
+ })
42
+ //]]>
43
+ </script>
44
+
45
+ <div id="messages"></div>
46
+
47
+ <div class="content-header">
48
+ <div class="content-buttons-placeholder"><p class="content-buttons form-buttons"><button type="button" class="scalable button-save-sortable"><span><?php echo $this->__('Save Products Position'); ?></span></button></p></div>
49
+ </div>
50
+
51
+ <div id="sortlist-wrapper">
52
+ <ul id="sortlist">
53
+ <?php foreach ($_products->getItems() as $_product): ?>
54
+ <li id="product_<?php echo $_product->getId(); ?>" class="sorting">
55
+ <div class="product_container">
56
+
57
+ <?php
58
+ # Error message for no products with image
59
+ # core/Mage/Catalog/Helper/Image.php(163): Mage_Catalog_Model_Product_Image->setBaseFile('no_selection')
60
+
61
+ try {
62
+ $imageUrl = $this->helper('catalog/image')->init($_product, 'thumbnail')->resize($this->getImageWidth());
63
+ } catch(Exception $e) {
64
+ $imageUrl = Mage::getDesign()->getSkinUrl('mageho/sortproducts/placeholder.jpg');
65
+ }
66
+ ?>
67
+
68
+ <img src="<?php echo $imageUrl; ?>" width="<?php echo $this->getImageWidth(); ?>" alt="<?php echo $this->htmlEscape($_product->getName()); ?>" class="sorting-image" longdesc="<?php echo $this->getProductInfoUrl($_product); ?>" <?php if ($this->isDisabled($_product)): ?>style="opacity:0.3"<?php endif; ?> />
69
+ <?php if (!$_product->isSaleable() && !$_product->isConfigurable()): ?><div class="saleable"><?php echo $this->helper('catalog')->__('Out of stock'); ?></div><?php endif; ?>
70
+ </div>
71
+ </li>
72
+ <?php endforeach; ?>
73
+ </ul>
74
+ </div>
75
+
76
+ <script type="text/javascript">
77
+ //<![CDATA[
78
+ $$('.sorting-image').each(function(image) {
79
+ image.addTip({ title: image.alt, style: 'slick', ajax: { url: image.readAttribute('longdesc'), options: { parameters: { form_key: '<?php echo $this->getFormKey(); ?>'} } } });
80
+ });
81
+ //]]>
82
+ </script>
83
+ <?php endif; ?>
app/design/adminhtml/default/default/template/mageho/sortproducts/tab.phtml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * @category Mageho
4
+ * @package Mageho_Sortproducts
5
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
6
+ * @license http://www.mageho.com/license Proprietary License
7
+ * @version 1.0.0
8
+ */
9
+ ?>
10
+ <div class="entry-edit">
11
+ <div class="entry-edit-head">
12
+ <h4 class="icon-head head-edit-form fieldset-legend"><?php echo $this->__('Sort Products'); ?></h4>
13
+ <div class="form-buttons"></div>
14
+ </div>
15
+
16
+ <div class="fieldset">
17
+ <iframe id="sortproductsdraganddrop" src="<?php echo $this->getSortProductsUrl(); ?>" width="100%" height="500" scrolling="yes" allowtransparency="true" frameborder="0" style="overflow:auto;"></iframe>
18
+ </div>
19
+ </div>
app/etc/modules/Mageho_All.xml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_All
6
+ * @copyright Copyright (c) 2012 - 2013 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+ -->
10
+ <config>
11
+ <modules>
12
+ <Mageho_All>
13
+ <active>true</active>
14
+ <codePool>local</codePool>
15
+ </Mageho_All>
16
+ </modules>
17
+ </config>
app/etc/modules/Mageho_Sortproducts.xml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_Sortproducts
6
+ * @copyright Copyright (c) 2012 Mageho (http://www.mageho.com)
7
+ * @license http://www.mageho.com/license Proprietary License
8
+ */
9
+ -->
10
+ <config>
11
+ <modules>
12
+ <Mageho_Sortproducts>
13
+ <active>true</active>
14
+ <codePool>local</codePool>
15
+ <depends>
16
+ <Mage_Adminhtml />
17
+ <Mage_Catalog />
18
+ </depends>
19
+ </Mageho_Sortproducts>
20
+ </modules>
21
+ </config>
app/locale/fr_FR/Mageho_All.csv ADDED
@@ -0,0 +1 @@
 
1
+ "Installed Mageho Extensions","Extensions Mageho installées"
app/locale/fr_FR/Mageho_Sortproducts.csv ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "%d position's product have been saved.","%s positions de produit ont �t� sauvegard�es."
2
+ "Enter 0 for no limit - This parameter can improve performance loading","Entrer 0 pour aucune limitation - Ce param�tre peut am�liorer les performances de chargement."
3
+ "Display Only Enabled Products","Afficher seulement les produits dont le statut est activ�"
4
+ "Display Out of Stock Products","Afficher les produits en rupture de stock"
5
+ "Image Width","Taille d'image"
6
+ "Limit number of products in iframe","Limiter le nombre de produits"
7
+ "General","G�n�ral"
8
+ "None","Aucun"
9
+ "Product Type (simple, bundle, etc)","Type produit (simple, bundle, etc)"
10
+ "Save Products Position","Sauvegarder la position des produits"
11
+ "Show Attributes","Afficher les attributs produits suivants"
12
+ "Sort Products","Trier les produits"
13
+ "Sort Category Products","Merchandising produits"
14
+ "The position of products has been saved with success.","La position des produits a bien �t� sauvegard�e avec succ�s."
js/mageho/sortproducts/builder.js ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // script.aculo.us builder.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
2
+
3
+ // Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ //
5
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
6
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
7
+
8
+ var Builder = {
9
+ NODEMAP: {
10
+ AREA: 'map',
11
+ CAPTION: 'table',
12
+ COL: 'table',
13
+ COLGROUP: 'table',
14
+ LEGEND: 'fieldset',
15
+ OPTGROUP: 'select',
16
+ OPTION: 'select',
17
+ PARAM: 'object',
18
+ TBODY: 'table',
19
+ TD: 'table',
20
+ TFOOT: 'table',
21
+ TH: 'table',
22
+ THEAD: 'table',
23
+ TR: 'table'
24
+ },
25
+ // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
26
+ // due to a Firefox bug
27
+ node: function(elementName) {
28
+ elementName = elementName.toUpperCase();
29
+
30
+ // try innerHTML approach
31
+ var parentTag = this.NODEMAP[elementName] || 'div';
32
+ var parentElement = document.createElement(parentTag);
33
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
34
+ parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
35
+ } catch(e) {}
36
+ var element = parentElement.firstChild || null;
37
+
38
+ // see if browser added wrapping tags
39
+ if(element && (element.tagName.toUpperCase() != elementName))
40
+ element = element.getElementsByTagName(elementName)[0];
41
+
42
+ // fallback to createElement approach
43
+ if(!element) element = document.createElement(elementName);
44
+
45
+ // abort if nothing could be created
46
+ if(!element) return;
47
+
48
+ // attributes (or text)
49
+ if(arguments[1])
50
+ if(this._isStringOrNumber(arguments[1]) ||
51
+ (arguments[1] instanceof Array) ||
52
+ arguments[1].tagName) {
53
+ this._children(element, arguments[1]);
54
+ } else {
55
+ var attrs = this._attributes(arguments[1]);
56
+ if(attrs.length) {
57
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
58
+ parentElement.innerHTML = "<" +elementName + " " +
59
+ attrs + "></" + elementName + ">";
60
+ } catch(e) {}
61
+ element = parentElement.firstChild || null;
62
+ // workaround firefox 1.0.X bug
63
+ if(!element) {
64
+ element = document.createElement(elementName);
65
+ for(attr in arguments[1])
66
+ element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
67
+ }
68
+ if(element.tagName.toUpperCase() != elementName)
69
+ element = parentElement.getElementsByTagName(elementName)[0];
70
+ }
71
+ }
72
+
73
+ // text, or array of children
74
+ if(arguments[2])
75
+ this._children(element, arguments[2]);
76
+
77
+ return $(element);
78
+ },
79
+ _text: function(text) {
80
+ return document.createTextNode(text);
81
+ },
82
+
83
+ ATTR_MAP: {
84
+ 'className': 'class',
85
+ 'htmlFor': 'for'
86
+ },
87
+
88
+ _attributes: function(attributes) {
89
+ var attrs = [];
90
+ for(attribute in attributes)
91
+ attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
92
+ '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
93
+ return attrs.join(" ");
94
+ },
95
+ _children: function(element, children) {
96
+ if(children.tagName) {
97
+ element.appendChild(children);
98
+ return;
99
+ }
100
+ if(typeof children=='object') { // array can hold nodes and text
101
+ children.flatten().each( function(e) {
102
+ if(typeof e=='object')
103
+ element.appendChild(e);
104
+ else
105
+ if(Builder._isStringOrNumber(e))
106
+ element.appendChild(Builder._text(e));
107
+ });
108
+ } else
109
+ if(Builder._isStringOrNumber(children))
110
+ element.appendChild(Builder._text(children));
111
+ },
112
+ _isStringOrNumber: function(param) {
113
+ return(typeof param=='string' || typeof param=='number');
114
+ },
115
+ build: function(html) {
116
+ var element = this.node('div');
117
+ $(element).update(html.strip());
118
+ return element.down();
119
+ },
120
+ dump: function(scope) {
121
+ if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
122
+
123
+ var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
124
+ "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
125
+ "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
126
+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
127
+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
128
+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
129
+
130
+ tags.each( function(tag){
131
+ scope[tag] = function() {
132
+ return Builder.node.apply(Builder, [tag].concat($A(arguments)));
133
+ };
134
+ });
135
+ }
136
+ };
js/mageho/sortproducts/controls.js ADDED
@@ -0,0 +1,965 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // script.aculo.us controls.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
2
+
3
+ // Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ // (c) 2005-2010 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
5
+ // (c) 2005-2010 Jon Tirsen (http://www.tirsen.com)
6
+ // Contributors:
7
+ // Richard Livsey
8
+ // Rahul Bhargava
9
+ // Rob Wills
10
+ //
11
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
12
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
13
+
14
+ // Autocompleter.Base handles all the autocompletion functionality
15
+ // that's independent of the data source for autocompletion. This
16
+ // includes drawing the autocompletion menu, observing keyboard
17
+ // and mouse events, and similar.
18
+ //
19
+ // Specific autocompleters need to provide, at the very least,
20
+ // a getUpdatedChoices function that will be invoked every time
21
+ // the text inside the monitored textbox changes. This method
22
+ // should get the text for which to provide autocompletion by
23
+ // invoking this.getToken(), NOT by directly accessing
24
+ // this.element.value. This is to allow incremental tokenized
25
+ // autocompletion. Specific auto-completion logic (AJAX, etc)
26
+ // belongs in getUpdatedChoices.
27
+ //
28
+ // Tokenized incremental autocompletion is enabled automatically
29
+ // when an autocompleter is instantiated with the 'tokens' option
30
+ // in the options parameter, e.g.:
31
+ // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
32
+ // will incrementally autocomplete with a comma as the token.
33
+ // Additionally, ',' in the above example can be replaced with
34
+ // a token array, e.g. { tokens: [',', '\n'] } which
35
+ // enables autocompletion on multiple tokens. This is most
36
+ // useful when one of the tokens is \n (a newline), as it
37
+ // allows smart autocompletion after linebreaks.
38
+
39
+ if(typeof Effect == 'undefined')
40
+ throw("controls.js requires including script.aculo.us' effects.js library");
41
+
42
+ var Autocompleter = { };
43
+ Autocompleter.Base = Class.create({
44
+ baseInitialize: function(element, update, options) {
45
+ element = $(element);
46
+ this.element = element;
47
+ this.update = $(update);
48
+ this.hasFocus = false;
49
+ this.changed = false;
50
+ this.active = false;
51
+ this.index = 0;
52
+ this.entryCount = 0;
53
+ this.oldElementValue = this.element.value;
54
+
55
+ if(this.setOptions)
56
+ this.setOptions(options);
57
+ else
58
+ this.options = options || { };
59
+
60
+ this.options.paramName = this.options.paramName || this.element.name;
61
+ this.options.tokens = this.options.tokens || [];
62
+ this.options.frequency = this.options.frequency || 0.4;
63
+ this.options.minChars = this.options.minChars || 1;
64
+ this.options.onShow = this.options.onShow ||
65
+ function(element, update){
66
+ if(!update.style.position || update.style.position=='absolute') {
67
+ update.style.position = 'absolute';
68
+ Position.clone(element, update, {
69
+ setHeight: false,
70
+ offsetTop: element.offsetHeight
71
+ });
72
+ }
73
+ Effect.Appear(update,{duration:0.15});
74
+ };
75
+ this.options.onHide = this.options.onHide ||
76
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
77
+
78
+ if(typeof(this.options.tokens) == 'string')
79
+ this.options.tokens = new Array(this.options.tokens);
80
+ // Force carriage returns as token delimiters anyway
81
+ if (!this.options.tokens.include('\n'))
82
+ this.options.tokens.push('\n');
83
+
84
+ this.observer = null;
85
+
86
+ this.element.setAttribute('autocomplete','off');
87
+
88
+ Element.hide(this.update);
89
+
90
+ Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
91
+ Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
92
+ },
93
+
94
+ show: function() {
95
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
96
+ if(!this.iefix &&
97
+ (Prototype.Browser.IE) &&
98
+ (Element.getStyle(this.update, 'position')=='absolute')) {
99
+ new Insertion.After(this.update,
100
+ '<iframe id="' + this.update.id + '_iefix" '+
101
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
102
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
103
+ this.iefix = $(this.update.id+'_iefix');
104
+ }
105
+ if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
106
+ },
107
+
108
+ fixIEOverlapping: function() {
109
+ Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
110
+ this.iefix.style.zIndex = 1;
111
+ this.update.style.zIndex = 2;
112
+ Element.show(this.iefix);
113
+ },
114
+
115
+ hide: function() {
116
+ this.stopIndicator();
117
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
118
+ if(this.iefix) Element.hide(this.iefix);
119
+ },
120
+
121
+ startIndicator: function() {
122
+ if(this.options.indicator) Element.show(this.options.indicator);
123
+ },
124
+
125
+ stopIndicator: function() {
126
+ if(this.options.indicator) Element.hide(this.options.indicator);
127
+ },
128
+
129
+ onKeyPress: function(event) {
130
+ if(this.active)
131
+ switch(event.keyCode) {
132
+ case Event.KEY_TAB:
133
+ case Event.KEY_RETURN:
134
+ this.selectEntry();
135
+ Event.stop(event);
136
+ case Event.KEY_ESC:
137
+ this.hide();
138
+ this.active = false;
139
+ Event.stop(event);
140
+ return;
141
+ case Event.KEY_LEFT:
142
+ case Event.KEY_RIGHT:
143
+ return;
144
+ case Event.KEY_UP:
145
+ this.markPrevious();
146
+ this.render();
147
+ Event.stop(event);
148
+ return;
149
+ case Event.KEY_DOWN:
150
+ this.markNext();
151
+ this.render();
152
+ Event.stop(event);
153
+ return;
154
+ }
155
+ else
156
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
157
+ (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
158
+
159
+ this.changed = true;
160
+ this.hasFocus = true;
161
+
162
+ if(this.observer) clearTimeout(this.observer);
163
+ this.observer =
164
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
165
+ },
166
+
167
+ activate: function() {
168
+ this.changed = false;
169
+ this.hasFocus = true;
170
+ this.getUpdatedChoices();
171
+ },
172
+
173
+ onHover: function(event) {
174
+ var element = Event.findElement(event, 'LI');
175
+ if(this.index != element.autocompleteIndex)
176
+ {
177
+ this.index = element.autocompleteIndex;
178
+ this.render();
179
+ }
180
+ Event.stop(event);
181
+ },
182
+
183
+ onClick: function(event) {
184
+ var element = Event.findElement(event, 'LI');
185
+ this.index = element.autocompleteIndex;
186
+ this.selectEntry();
187
+ this.hide();
188
+ },
189
+
190
+ onBlur: function(event) {
191
+ // needed to make click events working
192
+ setTimeout(this.hide.bind(this), 250);
193
+ this.hasFocus = false;
194
+ this.active = false;
195
+ },
196
+
197
+ render: function() {
198
+ if(this.entryCount > 0) {
199
+ for (var i = 0; i < this.entryCount; i++)
200
+ this.index==i ?
201
+ Element.addClassName(this.getEntry(i),"selected") :
202
+ Element.removeClassName(this.getEntry(i),"selected");
203
+ if(this.hasFocus) {
204
+ this.show();
205
+ this.active = true;
206
+ }
207
+ } else {
208
+ this.active = false;
209
+ this.hide();
210
+ }
211
+ },
212
+
213
+ markPrevious: function() {
214
+ if(this.index > 0) this.index--;
215
+ else this.index = this.entryCount-1;
216
+ this.getEntry(this.index).scrollIntoView(true);
217
+ },
218
+
219
+ markNext: function() {
220
+ if(this.index < this.entryCount-1) this.index++;
221
+ else this.index = 0;
222
+ this.getEntry(this.index).scrollIntoView(false);
223
+ },
224
+
225
+ getEntry: function(index) {
226
+ return this.update.firstChild.childNodes[index];
227
+ },
228
+
229
+ getCurrentEntry: function() {
230
+ return this.getEntry(this.index);
231
+ },
232
+
233
+ selectEntry: function() {
234
+ this.active = false;
235
+ this.updateElement(this.getCurrentEntry());
236
+ },
237
+
238
+ updateElement: function(selectedElement) {
239
+ if (this.options.updateElement) {
240
+ this.options.updateElement(selectedElement);
241
+ return;
242
+ }
243
+ var value = '';
244
+ if (this.options.select) {
245
+ var nodes = $(selectedElement).select('.' + this.options.select) || [];
246
+ if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
247
+ } else
248
+ value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
249
+
250
+ var bounds = this.getTokenBounds();
251
+ if (bounds[0] != -1) {
252
+ var newValue = this.element.value.substr(0, bounds[0]);
253
+ var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
254
+ if (whitespace)
255
+ newValue += whitespace[0];
256
+ this.element.value = newValue + value + this.element.value.substr(bounds[1]);
257
+ } else {
258
+ this.element.value = value;
259
+ }
260
+ this.oldElementValue = this.element.value;
261
+ this.element.focus();
262
+
263
+ if (this.options.afterUpdateElement)
264
+ this.options.afterUpdateElement(this.element, selectedElement);
265
+ },
266
+
267
+ updateChoices: function(choices) {
268
+ if(!this.changed && this.hasFocus) {
269
+ this.update.innerHTML = choices;
270
+ Element.cleanWhitespace(this.update);
271
+ Element.cleanWhitespace(this.update.down());
272
+
273
+ if(this.update.firstChild && this.update.down().childNodes) {
274
+ this.entryCount =
275
+ this.update.down().childNodes.length;
276
+ for (var i = 0; i < this.entryCount; i++) {
277
+ var entry = this.getEntry(i);
278
+ entry.autocompleteIndex = i;
279
+ this.addObservers(entry);
280
+ }
281
+ } else {
282
+ this.entryCount = 0;
283
+ }
284
+
285
+ this.stopIndicator();
286
+ this.index = 0;
287
+
288
+ if(this.entryCount==1 && this.options.autoSelect) {
289
+ this.selectEntry();
290
+ this.hide();
291
+ } else {
292
+ this.render();
293
+ }
294
+ }
295
+ },
296
+
297
+ addObservers: function(element) {
298
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
299
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
300
+ },
301
+
302
+ onObserverEvent: function() {
303
+ this.changed = false;
304
+ this.tokenBounds = null;
305
+ if(this.getToken().length>=this.options.minChars) {
306
+ this.getUpdatedChoices();
307
+ } else {
308
+ this.active = false;
309
+ this.hide();
310
+ }
311
+ this.oldElementValue = this.element.value;
312
+ },
313
+
314
+ getToken: function() {
315
+ var bounds = this.getTokenBounds();
316
+ return this.element.value.substring(bounds[0], bounds[1]).strip();
317
+ },
318
+
319
+ getTokenBounds: function() {
320
+ if (null != this.tokenBounds) return this.tokenBounds;
321
+ var value = this.element.value;
322
+ if (value.strip().empty()) return [-1, 0];
323
+ var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
324
+ var offset = (diff == this.oldElementValue.length ? 1 : 0);
325
+ var prevTokenPos = -1, nextTokenPos = value.length;
326
+ var tp;
327
+ for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
328
+ tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
329
+ if (tp > prevTokenPos) prevTokenPos = tp;
330
+ tp = value.indexOf(this.options.tokens[index], diff + offset);
331
+ if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
332
+ }
333
+ return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
334
+ }
335
+ });
336
+
337
+ Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
338
+ var boundary = Math.min(newS.length, oldS.length);
339
+ for (var index = 0; index < boundary; ++index)
340
+ if (newS[index] != oldS[index])
341
+ return index;
342
+ return boundary;
343
+ };
344
+
345
+ Ajax.Autocompleter = Class.create(Autocompleter.Base, {
346
+ initialize: function(element, update, url, options) {
347
+ this.baseInitialize(element, update, options);
348
+ this.options.asynchronous = true;
349
+ this.options.onComplete = this.onComplete.bind(this);
350
+ this.options.defaultParams = this.options.parameters || null;
351
+ this.url = url;
352
+ },
353
+
354
+ getUpdatedChoices: function() {
355
+ this.startIndicator();
356
+
357
+ var entry = encodeURIComponent(this.options.paramName) + '=' +
358
+ encodeURIComponent(this.getToken());
359
+
360
+ this.options.parameters = this.options.callback ?
361
+ this.options.callback(this.element, entry) : entry;
362
+
363
+ if(this.options.defaultParams)
364
+ this.options.parameters += '&' + this.options.defaultParams;
365
+
366
+ new Ajax.Request(this.url, this.options);
367
+ },
368
+
369
+ onComplete: function(request) {
370
+ this.updateChoices(request.responseText);
371
+ }
372
+ });
373
+
374
+ // The local array autocompleter. Used when you'd prefer to
375
+ // inject an array of autocompletion options into the page, rather
376
+ // than sending out Ajax queries, which can be quite slow sometimes.
377
+ //
378
+ // The constructor takes four parameters. The first two are, as usual,
379
+ // the id of the monitored textbox, and id of the autocompletion menu.
380
+ // The third is the array you want to autocomplete from, and the fourth
381
+ // is the options block.
382
+ //
383
+ // Extra local autocompletion options:
384
+ // - choices - How many autocompletion choices to offer
385
+ //
386
+ // - partialSearch - If false, the autocompleter will match entered
387
+ // text only at the beginning of strings in the
388
+ // autocomplete array. Defaults to true, which will
389
+ // match text at the beginning of any *word* in the
390
+ // strings in the autocomplete array. If you want to
391
+ // search anywhere in the string, additionally set
392
+ // the option fullSearch to true (default: off).
393
+ //
394
+ // - fullSsearch - Search anywhere in autocomplete array strings.
395
+ //
396
+ // - partialChars - How many characters to enter before triggering
397
+ // a partial match (unlike minChars, which defines
398
+ // how many characters are required to do any match
399
+ // at all). Defaults to 2.
400
+ //
401
+ // - ignoreCase - Whether to ignore case when autocompleting.
402
+ // Defaults to true.
403
+ //
404
+ // It's possible to pass in a custom function as the 'selector'
405
+ // option, if you prefer to write your own autocompletion logic.
406
+ // In that case, the other options above will not apply unless
407
+ // you support them.
408
+
409
+ Autocompleter.Local = Class.create(Autocompleter.Base, {
410
+ initialize: function(element, update, array, options) {
411
+ this.baseInitialize(element, update, options);
412
+ this.options.array = array;
413
+ },
414
+
415
+ getUpdatedChoices: function() {
416
+ this.updateChoices(this.options.selector(this));
417
+ },
418
+
419
+ setOptions: function(options) {
420
+ this.options = Object.extend({
421
+ choices: 10,
422
+ partialSearch: true,
423
+ partialChars: 2,
424
+ ignoreCase: true,
425
+ fullSearch: false,
426
+ selector: function(instance) {
427
+ var ret = []; // Beginning matches
428
+ var partial = []; // Inside matches
429
+ var entry = instance.getToken();
430
+ var count = 0;
431
+
432
+ for (var i = 0; i < instance.options.array.length &&
433
+ ret.length < instance.options.choices ; i++) {
434
+
435
+ var elem = instance.options.array[i];
436
+ var foundPos = instance.options.ignoreCase ?
437
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
438
+ elem.indexOf(entry);
439
+
440
+ while (foundPos != -1) {
441
+ if (foundPos == 0 && elem.length != entry.length) {
442
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
443
+ elem.substr(entry.length) + "</li>");
444
+ break;
445
+ } else if (entry.length >= instance.options.partialChars &&
446
+ instance.options.partialSearch && foundPos != -1) {
447
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
448
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
449
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
450
+ foundPos + entry.length) + "</li>");
451
+ break;
452
+ }
453
+ }
454
+
455
+ foundPos = instance.options.ignoreCase ?
456
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
457
+ elem.indexOf(entry, foundPos + 1);
458
+
459
+ }
460
+ }
461
+ if (partial.length)
462
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
463
+ return "<ul>" + ret.join('') + "</ul>";
464
+ }
465
+ }, options || { });
466
+ }
467
+ });
468
+
469
+ // AJAX in-place editor and collection editor
470
+ // Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
471
+
472
+ // Use this if you notice weird scrolling problems on some browsers,
473
+ // the DOM might be a bit confused when this gets called so do this
474
+ // waits 1 ms (with setTimeout) until it does the activation
475
+ Field.scrollFreeActivate = function(field) {
476
+ setTimeout(function() {
477
+ Field.activate(field);
478
+ }, 1);
479
+ };
480
+
481
+ Ajax.InPlaceEditor = Class.create({
482
+ initialize: function(element, url, options) {
483
+ this.url = url;
484
+ this.element = element = $(element);
485
+ this.prepareOptions();
486
+ this._controls = { };
487
+ arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
488
+ Object.extend(this.options, options || { });
489
+ if (!this.options.formId && this.element.id) {
490
+ this.options.formId = this.element.id + '-inplaceeditor';
491
+ if ($(this.options.formId))
492
+ this.options.formId = '';
493
+ }
494
+ if (this.options.externalControl)
495
+ this.options.externalControl = $(this.options.externalControl);
496
+ if (!this.options.externalControl)
497
+ this.options.externalControlOnly = false;
498
+ this._originalBackground = this.element.getStyle('background-color') || 'transparent';
499
+ this.element.title = this.options.clickToEditText;
500
+ this._boundCancelHandler = this.handleFormCancellation.bind(this);
501
+ this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
502
+ this._boundFailureHandler = this.handleAJAXFailure.bind(this);
503
+ this._boundSubmitHandler = this.handleFormSubmission.bind(this);
504
+ this._boundWrapperHandler = this.wrapUp.bind(this);
505
+ this.registerListeners();
506
+ },
507
+ checkForEscapeOrReturn: function(e) {
508
+ if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
509
+ if (Event.KEY_ESC == e.keyCode)
510
+ this.handleFormCancellation(e);
511
+ else if (Event.KEY_RETURN == e.keyCode)
512
+ this.handleFormSubmission(e);
513
+ },
514
+ createControl: function(mode, handler, extraClasses) {
515
+ var control = this.options[mode + 'Control'];
516
+ var text = this.options[mode + 'Text'];
517
+ if ('button' == control) {
518
+ var btn = document.createElement('input');
519
+ btn.type = 'submit';
520
+ btn.value = text;
521
+ btn.className = 'editor_' + mode + '_button';
522
+ if ('cancel' == mode)
523
+ btn.onclick = this._boundCancelHandler;
524
+ this._form.appendChild(btn);
525
+ this._controls[mode] = btn;
526
+ } else if ('link' == control) {
527
+ var link = document.createElement('a');
528
+ link.href = '#';
529
+ link.appendChild(document.createTextNode(text));
530
+ link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
531
+ link.className = 'editor_' + mode + '_link';
532
+ if (extraClasses)
533
+ link.className += ' ' + extraClasses;
534
+ this._form.appendChild(link);
535
+ this._controls[mode] = link;
536
+ }
537
+ },
538
+ createEditField: function() {
539
+ var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
540
+ var fld;
541
+ if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
542
+ fld = document.createElement('input');
543
+ fld.type = 'text';
544
+ var size = this.options.size || this.options.cols || 0;
545
+ if (0 < size) fld.size = size;
546
+ } else {
547
+ fld = document.createElement('textarea');
548
+ fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
549
+ fld.cols = this.options.cols || 40;
550
+ }
551
+ fld.name = this.options.paramName;
552
+ fld.value = text; // No HTML breaks conversion anymore
553
+ fld.className = 'editor_field';
554
+ if (this.options.submitOnBlur)
555
+ fld.onblur = this._boundSubmitHandler;
556
+ this._controls.editor = fld;
557
+ if (this.options.loadTextURL)
558
+ this.loadExternalText();
559
+ this._form.appendChild(this._controls.editor);
560
+ },
561
+ createForm: function() {
562
+ var ipe = this;
563
+ function addText(mode, condition) {
564
+ var text = ipe.options['text' + mode + 'Controls'];
565
+ if (!text || condition === false) return;
566
+ ipe._form.appendChild(document.createTextNode(text));
567
+ };
568
+ this._form = $(document.createElement('form'));
569
+ this._form.id = this.options.formId;
570
+ this._form.addClassName(this.options.formClassName);
571
+ this._form.onsubmit = this._boundSubmitHandler;
572
+ this.createEditField();
573
+ if ('textarea' == this._controls.editor.tagName.toLowerCase())
574
+ this._form.appendChild(document.createElement('br'));
575
+ if (this.options.onFormCustomization)
576
+ this.options.onFormCustomization(this, this._form);
577
+ addText('Before', this.options.okControl || this.options.cancelControl);
578
+ this.createControl('ok', this._boundSubmitHandler);
579
+ addText('Between', this.options.okControl && this.options.cancelControl);
580
+ this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
581
+ addText('After', this.options.okControl || this.options.cancelControl);
582
+ },
583
+ destroy: function() {
584
+ if (this._oldInnerHTML)
585
+ this.element.innerHTML = this._oldInnerHTML;
586
+ this.leaveEditMode();
587
+ this.unregisterListeners();
588
+ },
589
+ enterEditMode: function(e) {
590
+ if (this._saving || this._editing) return;
591
+ this._editing = true;
592
+ this.triggerCallback('onEnterEditMode');
593
+ if (this.options.externalControl)
594
+ this.options.externalControl.hide();
595
+ this.element.hide();
596
+ this.createForm();
597
+ this.element.parentNode.insertBefore(this._form, this.element);
598
+ if (!this.options.loadTextURL)
599
+ this.postProcessEditField();
600
+ if (e) Event.stop(e);
601
+ },
602
+ enterHover: function(e) {
603
+ if (this.options.hoverClassName)
604
+ this.element.addClassName(this.options.hoverClassName);
605
+ if (this._saving) return;
606
+ this.triggerCallback('onEnterHover');
607
+ },
608
+ getText: function() {
609
+ return this.element.innerHTML.unescapeHTML();
610
+ },
611
+ handleAJAXFailure: function(transport) {
612
+ this.triggerCallback('onFailure', transport);
613
+ if (this._oldInnerHTML) {
614
+ this.element.innerHTML = this._oldInnerHTML;
615
+ this._oldInnerHTML = null;
616
+ }
617
+ },
618
+ handleFormCancellation: function(e) {
619
+ this.wrapUp();
620
+ if (e) Event.stop(e);
621
+ },
622
+ handleFormSubmission: function(e) {
623
+ var form = this._form;
624
+ var value = $F(this._controls.editor);
625
+ this.prepareSubmission();
626
+ var params = this.options.callback(form, value) || '';
627
+ if (Object.isString(params))
628
+ params = params.toQueryParams();
629
+ params.editorId = this.element.id;
630
+ if (this.options.htmlResponse) {
631
+ var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
632
+ Object.extend(options, {
633
+ parameters: params,
634
+ onComplete: this._boundWrapperHandler,
635
+ onFailure: this._boundFailureHandler
636
+ });
637
+ new Ajax.Updater({ success: this.element }, this.url, options);
638
+ } else {
639
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
640
+ Object.extend(options, {
641
+ parameters: params,
642
+ onComplete: this._boundWrapperHandler,
643
+ onFailure: this._boundFailureHandler
644
+ });
645
+ new Ajax.Request(this.url, options);
646
+ }
647
+ if (e) Event.stop(e);
648
+ },
649
+ leaveEditMode: function() {
650
+ this.element.removeClassName(this.options.savingClassName);
651
+ this.removeForm();
652
+ this.leaveHover();
653
+ this.element.style.backgroundColor = this._originalBackground;
654
+ this.element.show();
655
+ if (this.options.externalControl)
656
+ this.options.externalControl.show();
657
+ this._saving = false;
658
+ this._editing = false;
659
+ this._oldInnerHTML = null;
660
+ this.triggerCallback('onLeaveEditMode');
661
+ },
662
+ leaveHover: function(e) {
663
+ if (this.options.hoverClassName)
664
+ this.element.removeClassName(this.options.hoverClassName);
665
+ if (this._saving) return;
666
+ this.triggerCallback('onLeaveHover');
667
+ },
668
+ loadExternalText: function() {
669
+ this._form.addClassName(this.options.loadingClassName);
670
+ this._controls.editor.disabled = true;
671
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
672
+ Object.extend(options, {
673
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
674
+ onComplete: Prototype.emptyFunction,
675
+ onSuccess: function(transport) {
676
+ this._form.removeClassName(this.options.loadingClassName);
677
+ var text = transport.responseText;
678
+ if (this.options.stripLoadedTextTags)
679
+ text = text.stripTags();
680
+ this._controls.editor.value = text;
681
+ this._controls.editor.disabled = false;
682
+ this.postProcessEditField();
683
+ }.bind(this),
684
+ onFailure: this._boundFailureHandler
685
+ });
686
+ new Ajax.Request(this.options.loadTextURL, options);
687
+ },
688
+ postProcessEditField: function() {
689
+ var fpc = this.options.fieldPostCreation;
690
+ if (fpc)
691
+ $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
692
+ },
693
+ prepareOptions: function() {
694
+ this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
695
+ Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
696
+ [this._extraDefaultOptions].flatten().compact().each(function(defs) {
697
+ Object.extend(this.options, defs);
698
+ }.bind(this));
699
+ },
700
+ prepareSubmission: function() {
701
+ this._saving = true;
702
+ this.removeForm();
703
+ this.leaveHover();
704
+ this.showSaving();
705
+ },
706
+ registerListeners: function() {
707
+ this._listeners = { };
708
+ var listener;
709
+ $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
710
+ listener = this[pair.value].bind(this);
711
+ this._listeners[pair.key] = listener;
712
+ if (!this.options.externalControlOnly)
713
+ this.element.observe(pair.key, listener);
714
+ if (this.options.externalControl)
715
+ this.options.externalControl.observe(pair.key, listener);
716
+ }.bind(this));
717
+ },
718
+ removeForm: function() {
719
+ if (!this._form) return;
720
+ this._form.remove();
721
+ this._form = null;
722
+ this._controls = { };
723
+ },
724
+ showSaving: function() {
725
+ this._oldInnerHTML = this.element.innerHTML;
726
+ this.element.innerHTML = this.options.savingText;
727
+ this.element.addClassName(this.options.savingClassName);
728
+ this.element.style.backgroundColor = this._originalBackground;
729
+ this.element.show();
730
+ },
731
+ triggerCallback: function(cbName, arg) {
732
+ if ('function' == typeof this.options[cbName]) {
733
+ this.options[cbName](this, arg);
734
+ }
735
+ },
736
+ unregisterListeners: function() {
737
+ $H(this._listeners).each(function(pair) {
738
+ if (!this.options.externalControlOnly)
739
+ this.element.stopObserving(pair.key, pair.value);
740
+ if (this.options.externalControl)
741
+ this.options.externalControl.stopObserving(pair.key, pair.value);
742
+ }.bind(this));
743
+ },
744
+ wrapUp: function(transport) {
745
+ this.leaveEditMode();
746
+ // Can't use triggerCallback due to backward compatibility: requires
747
+ // binding + direct element
748
+ this._boundComplete(transport, this.element);
749
+ }
750
+ });
751
+
752
+ Object.extend(Ajax.InPlaceEditor.prototype, {
753
+ dispose: Ajax.InPlaceEditor.prototype.destroy
754
+ });
755
+
756
+ Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
757
+ initialize: function($super, element, url, options) {
758
+ this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
759
+ $super(element, url, options);
760
+ },
761
+
762
+ createEditField: function() {
763
+ var list = document.createElement('select');
764
+ list.name = this.options.paramName;
765
+ list.size = 1;
766
+ this._controls.editor = list;
767
+ this._collection = this.options.collection || [];
768
+ if (this.options.loadCollectionURL)
769
+ this.loadCollection();
770
+ else
771
+ this.checkForExternalText();
772
+ this._form.appendChild(this._controls.editor);
773
+ },
774
+
775
+ loadCollection: function() {
776
+ this._form.addClassName(this.options.loadingClassName);
777
+ this.showLoadingText(this.options.loadingCollectionText);
778
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
779
+ Object.extend(options, {
780
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
781
+ onComplete: Prototype.emptyFunction,
782
+ onSuccess: function(transport) {
783
+ var js = transport.responseText.strip();
784
+ if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
785
+ throw('Server returned an invalid collection representation.');
786
+ this._collection = eval(js);
787
+ this.checkForExternalText();
788
+ }.bind(this),
789
+ onFailure: this.onFailure
790
+ });
791
+ new Ajax.Request(this.options.loadCollectionURL, options);
792
+ },
793
+
794
+ showLoadingText: function(text) {
795
+ this._controls.editor.disabled = true;
796
+ var tempOption = this._controls.editor.firstChild;
797
+ if (!tempOption) {
798
+ tempOption = document.createElement('option');
799
+ tempOption.value = '';
800
+ this._controls.editor.appendChild(tempOption);
801
+ tempOption.selected = true;
802
+ }
803
+ tempOption.update((text || '').stripScripts().stripTags());
804
+ },
805
+
806
+ checkForExternalText: function() {
807
+ this._text = this.getText();
808
+ if (this.options.loadTextURL)
809
+ this.loadExternalText();
810
+ else
811
+ this.buildOptionList();
812
+ },
813
+
814
+ loadExternalText: function() {
815
+ this.showLoadingText(this.options.loadingText);
816
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
817
+ Object.extend(options, {
818
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
819
+ onComplete: Prototype.emptyFunction,
820
+ onSuccess: function(transport) {
821
+ this._text = transport.responseText.strip();
822
+ this.buildOptionList();
823
+ }.bind(this),
824
+ onFailure: this.onFailure
825
+ });
826
+ new Ajax.Request(this.options.loadTextURL, options);
827
+ },
828
+
829
+ buildOptionList: function() {
830
+ this._form.removeClassName(this.options.loadingClassName);
831
+ this._collection = this._collection.map(function(entry) {
832
+ return 2 === entry.length ? entry : [entry, entry].flatten();
833
+ });
834
+ var marker = ('value' in this.options) ? this.options.value : this._text;
835
+ var textFound = this._collection.any(function(entry) {
836
+ return entry[0] == marker;
837
+ }.bind(this));
838
+ this._controls.editor.update('');
839
+ var option;
840
+ this._collection.each(function(entry, index) {
841
+ option = document.createElement('option');
842
+ option.value = entry[0];
843
+ option.selected = textFound ? entry[0] == marker : 0 == index;
844
+ option.appendChild(document.createTextNode(entry[1]));
845
+ this._controls.editor.appendChild(option);
846
+ }.bind(this));
847
+ this._controls.editor.disabled = false;
848
+ Field.scrollFreeActivate(this._controls.editor);
849
+ }
850
+ });
851
+
852
+ //**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
853
+ //**** This only exists for a while, in order to let ****
854
+ //**** users adapt to the new API. Read up on the new ****
855
+ //**** API and convert your code to it ASAP! ****
856
+
857
+ Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
858
+ if (!options) return;
859
+ function fallback(name, expr) {
860
+ if (name in options || expr === undefined) return;
861
+ options[name] = expr;
862
+ };
863
+ fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
864
+ options.cancelLink == options.cancelButton == false ? false : undefined)));
865
+ fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
866
+ options.okLink == options.okButton == false ? false : undefined)));
867
+ fallback('highlightColor', options.highlightcolor);
868
+ fallback('highlightEndColor', options.highlightendcolor);
869
+ };
870
+
871
+ Object.extend(Ajax.InPlaceEditor, {
872
+ DefaultOptions: {
873
+ ajaxOptions: { },
874
+ autoRows: 3, // Use when multi-line w/ rows == 1
875
+ cancelControl: 'link', // 'link'|'button'|false
876
+ cancelText: 'cancel',
877
+ clickToEditText: 'Click to edit',
878
+ externalControl: null, // id|elt
879
+ externalControlOnly: false,
880
+ fieldPostCreation: 'activate', // 'activate'|'focus'|false
881
+ formClassName: 'inplaceeditor-form',
882
+ formId: null, // id|elt
883
+ highlightColor: '#ffff99',
884
+ highlightEndColor: '#ffffff',
885
+ hoverClassName: '',
886
+ htmlResponse: true,
887
+ loadingClassName: 'inplaceeditor-loading',
888
+ loadingText: 'Loading...',
889
+ okControl: 'button', // 'link'|'button'|false
890
+ okText: 'ok',
891
+ paramName: 'value',
892
+ rows: 1, // If 1 and multi-line, uses autoRows
893
+ savingClassName: 'inplaceeditor-saving',
894
+ savingText: 'Saving...',
895
+ size: 0,
896
+ stripLoadedTextTags: false,
897
+ submitOnBlur: false,
898
+ textAfterControls: '',
899
+ textBeforeControls: '',
900
+ textBetweenControls: ''
901
+ },
902
+ DefaultCallbacks: {
903
+ callback: function(form) {
904
+ return Form.serialize(form);
905
+ },
906
+ onComplete: function(transport, element) {
907
+ // For backward compatibility, this one is bound to the IPE, and passes
908
+ // the element directly. It was too often customized, so we don't break it.
909
+ new Effect.Highlight(element, {
910
+ startcolor: this.options.highlightColor, keepBackgroundImage: true });
911
+ },
912
+ onEnterEditMode: null,
913
+ onEnterHover: function(ipe) {
914
+ ipe.element.style.backgroundColor = ipe.options.highlightColor;
915
+ if (ipe._effect)
916
+ ipe._effect.cancel();
917
+ },
918
+ onFailure: function(transport, ipe) {
919
+ alert('Error communication with the server: ' + transport.responseText.stripTags());
920
+ },
921
+ onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
922
+ onLeaveEditMode: null,
923
+ onLeaveHover: function(ipe) {
924
+ ipe._effect = new Effect.Highlight(ipe.element, {
925
+ startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
926
+ restorecolor: ipe._originalBackground, keepBackgroundImage: true
927
+ });
928
+ }
929
+ },
930
+ Listeners: {
931
+ click: 'enterEditMode',
932
+ keydown: 'checkForEscapeOrReturn',
933
+ mouseover: 'enterHover',
934
+ mouseout: 'leaveHover'
935
+ }
936
+ });
937
+
938
+ Ajax.InPlaceCollectionEditor.DefaultOptions = {
939
+ loadingCollectionText: 'Loading options...'
940
+ };
941
+
942
+ // Delayed observer, like Form.Element.Observer,
943
+ // but waits for delay after last key input
944
+ // Ideal for live-search fields
945
+
946
+ Form.Element.DelayedObserver = Class.create({
947
+ initialize: function(element, delay, callback) {
948
+ this.delay = delay || 0.5;
949
+ this.element = $(element);
950
+ this.callback = callback;
951
+ this.timer = null;
952
+ this.lastValue = $F(this.element);
953
+ Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
954
+ },
955
+ delayedListener: function(event) {
956
+ if(this.lastValue == $F(this.element)) return;
957
+ if(this.timer) clearTimeout(this.timer);
958
+ this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
959
+ this.lastValue = $F(this.element);
960
+ },
961
+ onTimerEvent: function() {
962
+ this.timer = null;
963
+ this.callback(this.element, $F(this.element));
964
+ }
965
+ });
js/mageho/sortproducts/dragdrop.js ADDED
@@ -0,0 +1,974 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // script.aculo.us dragdrop.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
2
+
3
+ // Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ //
5
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
6
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
7
+
8
+ if(Object.isUndefined(Effect))
9
+ throw("dragdrop.js requires including script.aculo.us' effects.js library");
10
+
11
+ var Droppables = {
12
+ drops: [],
13
+
14
+ remove: function(element) {
15
+ this.drops = this.drops.reject(function(d) { return d.element==$(element) });
16
+ },
17
+
18
+ add: function(element) {
19
+ element = $(element);
20
+ var options = Object.extend({
21
+ greedy: true,
22
+ hoverclass: null,
23
+ tree: false
24
+ }, arguments[1] || { });
25
+
26
+ // cache containers
27
+ if(options.containment) {
28
+ options._containers = [];
29
+ var containment = options.containment;
30
+ if(Object.isArray(containment)) {
31
+ containment.each( function(c) { options._containers.push($(c)) });
32
+ } else {
33
+ options._containers.push($(containment));
34
+ }
35
+ }
36
+
37
+ if(options.accept) options.accept = [options.accept].flatten();
38
+
39
+ Element.makePositioned(element); // fix IE
40
+ options.element = element;
41
+
42
+ this.drops.push(options);
43
+ },
44
+
45
+ findDeepestChild: function(drops) {
46
+ deepest = drops[0];
47
+
48
+ for (i = 1; i < drops.length; ++i)
49
+ if (Element.isParent(drops[i].element, deepest.element))
50
+ deepest = drops[i];
51
+
52
+ return deepest;
53
+ },
54
+
55
+ isContained: function(element, drop) {
56
+ var containmentNode;
57
+ if(drop.tree) {
58
+ containmentNode = element.treeNode;
59
+ } else {
60
+ containmentNode = element.parentNode;
61
+ }
62
+ return drop._containers.detect(function(c) { return containmentNode == c });
63
+ },
64
+
65
+ isAffected: function(point, element, drop) {
66
+ return (
67
+ (drop.element!=element) &&
68
+ ((!drop._containers) ||
69
+ this.isContained(element, drop)) &&
70
+ ((!drop.accept) ||
71
+ (Element.classNames(element).detect(
72
+ function(v) { return drop.accept.include(v) } ) )) &&
73
+ Position.within(drop.element, point[0], point[1]) );
74
+ },
75
+
76
+ deactivate: function(drop) {
77
+ if(drop.hoverclass)
78
+ Element.removeClassName(drop.element, drop.hoverclass);
79
+ this.last_active = null;
80
+ },
81
+
82
+ activate: function(drop) {
83
+ if(drop.hoverclass)
84
+ Element.addClassName(drop.element, drop.hoverclass);
85
+ this.last_active = drop;
86
+ },
87
+
88
+ show: function(point, element) {
89
+ if(!this.drops.length) return;
90
+ var drop, affected = [];
91
+
92
+ this.drops.each( function(drop) {
93
+ if(Droppables.isAffected(point, element, drop))
94
+ affected.push(drop);
95
+ });
96
+
97
+ if(affected.length>0)
98
+ drop = Droppables.findDeepestChild(affected);
99
+
100
+ if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
101
+ if (drop) {
102
+ Position.within(drop.element, point[0], point[1]);
103
+ if(drop.onHover)
104
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
105
+
106
+ if (drop != this.last_active) Droppables.activate(drop);
107
+ }
108
+ },
109
+
110
+ fire: function(event, element) {
111
+ if(!this.last_active) return;
112
+ Position.prepare();
113
+
114
+ if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
115
+ if (this.last_active.onDrop) {
116
+ this.last_active.onDrop(element, this.last_active.element, event);
117
+ return true;
118
+ }
119
+ },
120
+
121
+ reset: function() {
122
+ if(this.last_active)
123
+ this.deactivate(this.last_active);
124
+ }
125
+ };
126
+
127
+ var Draggables = {
128
+ drags: [],
129
+ observers: [],
130
+
131
+ register: function(draggable) {
132
+ if(this.drags.length == 0) {
133
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
134
+ this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
135
+ this.eventKeypress = this.keyPress.bindAsEventListener(this);
136
+
137
+ Event.observe(document, "mouseup", this.eventMouseUp);
138
+ Event.observe(document, "mousemove", this.eventMouseMove);
139
+ Event.observe(document, "keypress", this.eventKeypress);
140
+ }
141
+ this.drags.push(draggable);
142
+ },
143
+
144
+ unregister: function(draggable) {
145
+ this.drags = this.drags.reject(function(d) { return d==draggable });
146
+ if(this.drags.length == 0) {
147
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
148
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
149
+ Event.stopObserving(document, "keypress", this.eventKeypress);
150
+ }
151
+ },
152
+
153
+ activate: function(draggable) {
154
+ if(draggable.options.delay) {
155
+ this._timeout = setTimeout(function() {
156
+ Draggables._timeout = null;
157
+ window.focus();
158
+ Draggables.activeDraggable = draggable;
159
+ }.bind(this), draggable.options.delay);
160
+ } else {
161
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
162
+ this.activeDraggable = draggable;
163
+ }
164
+ },
165
+
166
+ deactivate: function() {
167
+ this.activeDraggable = null;
168
+ },
169
+
170
+ updateDrag: function(event) {
171
+ if(!this.activeDraggable) return;
172
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
173
+ // Mozilla-based browsers fire successive mousemove events with
174
+ // the same coordinates, prevent needless redrawing (moz bug?)
175
+ if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
176
+ this._lastPointer = pointer;
177
+
178
+ this.activeDraggable.updateDrag(event, pointer);
179
+ },
180
+
181
+ endDrag: function(event) {
182
+ if(this._timeout) {
183
+ clearTimeout(this._timeout);
184
+ this._timeout = null;
185
+ }
186
+ if(!this.activeDraggable) return;
187
+ this._lastPointer = null;
188
+ this.activeDraggable.endDrag(event);
189
+ this.activeDraggable = null;
190
+ },
191
+
192
+ keyPress: function(event) {
193
+ if(this.activeDraggable)
194
+ this.activeDraggable.keyPress(event);
195
+ },
196
+
197
+ addObserver: function(observer) {
198
+ this.observers.push(observer);
199
+ this._cacheObserverCallbacks();
200
+ },
201
+
202
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
203
+ this.observers = this.observers.reject( function(o) { return o.element==element });
204
+ this._cacheObserverCallbacks();
205
+ },
206
+
207
+ notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
208
+ if(this[eventName+'Count'] > 0)
209
+ this.observers.each( function(o) {
210
+ if(o[eventName]) o[eventName](eventName, draggable, event);
211
+ });
212
+ if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
213
+ },
214
+
215
+ _cacheObserverCallbacks: function() {
216
+ ['onStart','onEnd','onDrag'].each( function(eventName) {
217
+ Draggables[eventName+'Count'] = Draggables.observers.select(
218
+ function(o) { return o[eventName]; }
219
+ ).length;
220
+ });
221
+ }
222
+ };
223
+
224
+ /*--------------------------------------------------------------------------*/
225
+
226
+ var Draggable = Class.create({
227
+ initialize: function(element) {
228
+ var defaults = {
229
+ handle: false,
230
+ reverteffect: function(element, top_offset, left_offset) {
231
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
232
+ new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
233
+ queue: {scope:'_draggable', position:'end'}
234
+ });
235
+ },
236
+ endeffect: function(element) {
237
+ var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
238
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
239
+ queue: {scope:'_draggable', position:'end'},
240
+ afterFinish: function(){
241
+ Draggable._dragging[element] = false
242
+ }
243
+ });
244
+ },
245
+ zindex: 1000,
246
+ revert: false,
247
+ quiet: false,
248
+ scroll: false,
249
+ scrollSensitivity: 20,
250
+ scrollSpeed: 15,
251
+ snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
252
+ delay: 0
253
+ };
254
+
255
+ if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
256
+ Object.extend(defaults, {
257
+ starteffect: function(element) {
258
+ element._opacity = Element.getOpacity(element);
259
+ Draggable._dragging[element] = true;
260
+ new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
261
+ }
262
+ });
263
+
264
+ var options = Object.extend(defaults, arguments[1] || { });
265
+
266
+ this.element = $(element);
267
+
268
+ if(options.handle && Object.isString(options.handle))
269
+ this.handle = this.element.down('.'+options.handle, 0);
270
+
271
+ if(!this.handle) this.handle = $(options.handle);
272
+ if(!this.handle) this.handle = this.element;
273
+
274
+ if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
275
+ options.scroll = $(options.scroll);
276
+ this._isScrollChild = Element.childOf(this.element, options.scroll);
277
+ }
278
+
279
+ Element.makePositioned(this.element); // fix IE
280
+
281
+ this.options = options;
282
+ this.dragging = false;
283
+
284
+ this.eventMouseDown = this.initDrag.bindAsEventListener(this);
285
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
286
+
287
+ Draggables.register(this);
288
+ },
289
+
290
+ destroy: function() {
291
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
292
+ Draggables.unregister(this);
293
+ },
294
+
295
+ currentDelta: function() {
296
+ return([
297
+ parseInt(Element.getStyle(this.element,'left') || '0'),
298
+ parseInt(Element.getStyle(this.element,'top') || '0')]);
299
+ },
300
+
301
+ initDrag: function(event) {
302
+ if(!Object.isUndefined(Draggable._dragging[this.element]) &&
303
+ Draggable._dragging[this.element]) return;
304
+ if(Event.isLeftClick(event)) {
305
+ // abort on form elements, fixes a Firefox issue
306
+ var src = Event.element(event);
307
+ if((tag_name = src.tagName.toUpperCase()) && (
308
+ tag_name=='INPUT' ||
309
+ tag_name=='SELECT' ||
310
+ tag_name=='OPTION' ||
311
+ tag_name=='BUTTON' ||
312
+ tag_name=='TEXTAREA')) return;
313
+
314
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
315
+ var pos = this.element.cumulativeOffset();
316
+ this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
317
+
318
+ Draggables.activate(this);
319
+ Event.stop(event);
320
+ }
321
+ },
322
+
323
+ startDrag: function(event) {
324
+ this.dragging = true;
325
+ if(!this.delta)
326
+ this.delta = this.currentDelta();
327
+
328
+ if(this.options.zindex) {
329
+ this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
330
+ this.element.style.zIndex = this.options.zindex;
331
+ }
332
+
333
+ if(this.options.ghosting) {
334
+ this._clone = this.element.cloneNode(true);
335
+ this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
336
+ if (!this._originallyAbsolute)
337
+ Position.absolutize(this.element);
338
+ this.element.parentNode.insertBefore(this._clone, this.element);
339
+ }
340
+
341
+ if(this.options.scroll) {
342
+ if (this.options.scroll == window) {
343
+ var where = this._getWindowScroll(this.options.scroll);
344
+ this.originalScrollLeft = where.left;
345
+ this.originalScrollTop = where.top;
346
+ } else {
347
+ this.originalScrollLeft = this.options.scroll.scrollLeft;
348
+ this.originalScrollTop = this.options.scroll.scrollTop;
349
+ }
350
+ }
351
+
352
+ Draggables.notify('onStart', this, event);
353
+
354
+ if(this.options.starteffect) this.options.starteffect(this.element);
355
+ },
356
+
357
+ updateDrag: function(event, pointer) {
358
+ if(!this.dragging) this.startDrag(event);
359
+
360
+ if(!this.options.quiet){
361
+ Position.prepare();
362
+ Droppables.show(pointer, this.element);
363
+ }
364
+
365
+ Draggables.notify('onDrag', this, event);
366
+
367
+ this.draw(pointer);
368
+ if(this.options.change) this.options.change(this);
369
+
370
+ if(this.options.scroll) {
371
+ this.stopScrolling();
372
+
373
+ var p;
374
+ if (this.options.scroll == window) {
375
+ with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
376
+ } else {
377
+ p = Position.page(this.options.scroll).toArray();
378
+ p[0] += this.options.scroll.scrollLeft + Position.deltaX;
379
+ p[1] += this.options.scroll.scrollTop + Position.deltaY;
380
+ p.push(p[0]+this.options.scroll.offsetWidth);
381
+ p.push(p[1]+this.options.scroll.offsetHeight);
382
+ }
383
+ var speed = [0,0];
384
+ if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
385
+ if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
386
+ if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
387
+ if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
388
+ this.startScrolling(speed);
389
+ }
390
+
391
+ // fix AppleWebKit rendering
392
+ if(Prototype.Browser.WebKit) window.scrollBy(0,0);
393
+
394
+ Event.stop(event);
395
+ },
396
+
397
+ finishDrag: function(event, success) {
398
+ this.dragging = false;
399
+
400
+ if(this.options.quiet){
401
+ Position.prepare();
402
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
403
+ Droppables.show(pointer, this.element);
404
+ }
405
+
406
+ if(this.options.ghosting) {
407
+ if (!this._originallyAbsolute)
408
+ Position.relativize(this.element);
409
+ delete this._originallyAbsolute;
410
+ Element.remove(this._clone);
411
+ this._clone = null;
412
+ }
413
+
414
+ var dropped = false;
415
+ if(success) {
416
+ dropped = Droppables.fire(event, this.element);
417
+ if (!dropped) dropped = false;
418
+ }
419
+ if(dropped && this.options.onDropped) this.options.onDropped(this.element);
420
+ Draggables.notify('onEnd', this, event);
421
+
422
+ var revert = this.options.revert;
423
+ if(revert && Object.isFunction(revert)) revert = revert(this.element);
424
+
425
+ var d = this.currentDelta();
426
+ if(revert && this.options.reverteffect) {
427
+ if (dropped == 0 || revert != 'failure')
428
+ this.options.reverteffect(this.element,
429
+ d[1]-this.delta[1], d[0]-this.delta[0]);
430
+ } else {
431
+ this.delta = d;
432
+ }
433
+
434
+ if(this.options.zindex)
435
+ this.element.style.zIndex = this.originalZ;
436
+
437
+ if(this.options.endeffect)
438
+ this.options.endeffect(this.element);
439
+
440
+ Draggables.deactivate(this);
441
+ Droppables.reset();
442
+ },
443
+
444
+ keyPress: function(event) {
445
+ if(event.keyCode!=Event.KEY_ESC) return;
446
+ this.finishDrag(event, false);
447
+ Event.stop(event);
448
+ },
449
+
450
+ endDrag: function(event) {
451
+ if(!this.dragging) return;
452
+ this.stopScrolling();
453
+ this.finishDrag(event, true);
454
+ Event.stop(event);
455
+ },
456
+
457
+ draw: function(point) {
458
+ var pos = this.element.cumulativeOffset();
459
+ if(this.options.ghosting) {
460
+ var r = Position.realOffset(this.element);
461
+ pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
462
+ }
463
+
464
+ var d = this.currentDelta();
465
+ pos[0] -= d[0]; pos[1] -= d[1];
466
+
467
+ if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
468
+ pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
469
+ pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
470
+ }
471
+
472
+ var p = [0,1].map(function(i){
473
+ return (point[i]-pos[i]-this.offset[i])
474
+ }.bind(this));
475
+
476
+ if(this.options.snap) {
477
+ if(Object.isFunction(this.options.snap)) {
478
+ p = this.options.snap(p[0],p[1],this);
479
+ } else {
480
+ if(Object.isArray(this.options.snap)) {
481
+ p = p.map( function(v, i) {
482
+ return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
483
+ } else {
484
+ p = p.map( function(v) {
485
+ return (v/this.options.snap).round()*this.options.snap }.bind(this));
486
+ }
487
+ }}
488
+
489
+ var style = this.element.style;
490
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
491
+ style.left = p[0] + "px";
492
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
493
+ style.top = p[1] + "px";
494
+
495
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
496
+ },
497
+
498
+ stopScrolling: function() {
499
+ if(this.scrollInterval) {
500
+ clearInterval(this.scrollInterval);
501
+ this.scrollInterval = null;
502
+ Draggables._lastScrollPointer = null;
503
+ }
504
+ },
505
+
506
+ startScrolling: function(speed) {
507
+ if(!(speed[0] || speed[1])) return;
508
+ this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
509
+ this.lastScrolled = new Date();
510
+ this.scrollInterval = setInterval(this.scroll.bind(this), 10);
511
+ },
512
+
513
+ scroll: function() {
514
+ var current = new Date();
515
+ var delta = current - this.lastScrolled;
516
+ this.lastScrolled = current;
517
+ if(this.options.scroll == window) {
518
+ with (this._getWindowScroll(this.options.scroll)) {
519
+ if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
520
+ var d = delta / 1000;
521
+ this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
522
+ }
523
+ }
524
+ } else {
525
+ this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
526
+ this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
527
+ }
528
+
529
+ Position.prepare();
530
+ Droppables.show(Draggables._lastPointer, this.element);
531
+ Draggables.notify('onDrag', this);
532
+ if (this._isScrollChild) {
533
+ Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
534
+ Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
535
+ Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
536
+ if (Draggables._lastScrollPointer[0] < 0)
537
+ Draggables._lastScrollPointer[0] = 0;
538
+ if (Draggables._lastScrollPointer[1] < 0)
539
+ Draggables._lastScrollPointer[1] = 0;
540
+ this.draw(Draggables._lastScrollPointer);
541
+ }
542
+
543
+ if(this.options.change) this.options.change(this);
544
+ },
545
+
546
+ _getWindowScroll: function(w) {
547
+ var T, L, W, H;
548
+ with (w.document) {
549
+ if (w.document.documentElement && documentElement.scrollTop) {
550
+ T = documentElement.scrollTop;
551
+ L = documentElement.scrollLeft;
552
+ } else if (w.document.body) {
553
+ T = body.scrollTop;
554
+ L = body.scrollLeft;
555
+ }
556
+ if (w.innerWidth) {
557
+ W = w.innerWidth;
558
+ H = w.innerHeight;
559
+ } else if (w.document.documentElement && documentElement.clientWidth) {
560
+ W = documentElement.clientWidth;
561
+ H = documentElement.clientHeight;
562
+ } else {
563
+ W = body.offsetWidth;
564
+ H = body.offsetHeight;
565
+ }
566
+ }
567
+ return { top: T, left: L, width: W, height: H };
568
+ }
569
+ });
570
+
571
+ Draggable._dragging = { };
572
+
573
+ /*--------------------------------------------------------------------------*/
574
+
575
+ var SortableObserver = Class.create({
576
+ initialize: function(element, observer) {
577
+ this.element = $(element);
578
+ this.observer = observer;
579
+ this.lastValue = Sortable.serialize(this.element);
580
+ },
581
+
582
+ onStart: function() {
583
+ this.lastValue = Sortable.serialize(this.element);
584
+ },
585
+
586
+ onEnd: function() {
587
+ Sortable.unmark();
588
+ if(this.lastValue != Sortable.serialize(this.element))
589
+ this.observer(this.element)
590
+ }
591
+ });
592
+
593
+ var Sortable = {
594
+ SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
595
+
596
+ sortables: { },
597
+
598
+ _findRootElement: function(element) {
599
+ while (element.tagName.toUpperCase() != "BODY") {
600
+ if(element.id && Sortable.sortables[element.id]) return element;
601
+ element = element.parentNode;
602
+ }
603
+ },
604
+
605
+ options: function(element) {
606
+ element = Sortable._findRootElement($(element));
607
+ if(!element) return;
608
+ return Sortable.sortables[element.id];
609
+ },
610
+
611
+ destroy: function(element){
612
+ element = $(element);
613
+ var s = Sortable.sortables[element.id];
614
+
615
+ if(s) {
616
+ Draggables.removeObserver(s.element);
617
+ s.droppables.each(function(d){ Droppables.remove(d) });
618
+ s.draggables.invoke('destroy');
619
+
620
+ delete Sortable.sortables[s.element.id];
621
+ }
622
+ },
623
+
624
+ create: function(element) {
625
+ element = $(element);
626
+ var options = Object.extend({
627
+ element: element,
628
+ tag: 'li', // assumes li children, override with tag: 'tagname'
629
+ dropOnEmpty: false,
630
+ tree: false,
631
+ treeTag: 'ul',
632
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
633
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
634
+ containment: element, // also takes array of elements (or id's); or false
635
+ handle: false, // or a CSS class
636
+ only: false,
637
+ delay: 0,
638
+ hoverclass: null,
639
+ ghosting: false,
640
+ quiet: false,
641
+ scroll: false,
642
+ scrollSensitivity: 20,
643
+ scrollSpeed: 15,
644
+ format: this.SERIALIZE_RULE,
645
+
646
+ // these take arrays of elements or ids and can be
647
+ // used for better initialization performance
648
+ elements: false,
649
+ handles: false,
650
+
651
+ onChange: Prototype.emptyFunction,
652
+ onUpdate: Prototype.emptyFunction
653
+ }, arguments[1] || { });
654
+
655
+ // clear any old sortable with same element
656
+ this.destroy(element);
657
+
658
+ // build options for the draggables
659
+ var options_for_draggable = {
660
+ revert: true,
661
+ quiet: options.quiet,
662
+ scroll: options.scroll,
663
+ scrollSpeed: options.scrollSpeed,
664
+ scrollSensitivity: options.scrollSensitivity,
665
+ delay: options.delay,
666
+ ghosting: options.ghosting,
667
+ constraint: options.constraint,
668
+ handle: options.handle };
669
+
670
+ if(options.starteffect)
671
+ options_for_draggable.starteffect = options.starteffect;
672
+
673
+ if(options.reverteffect)
674
+ options_for_draggable.reverteffect = options.reverteffect;
675
+ else
676
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
677
+ element.style.top = 0;
678
+ element.style.left = 0;
679
+ };
680
+
681
+ if(options.endeffect)
682
+ options_for_draggable.endeffect = options.endeffect;
683
+
684
+ if(options.zindex)
685
+ options_for_draggable.zindex = options.zindex;
686
+
687
+ // build options for the droppables
688
+ var options_for_droppable = {
689
+ overlap: options.overlap,
690
+ containment: options.containment,
691
+ tree: options.tree,
692
+ hoverclass: options.hoverclass,
693
+ onHover: Sortable.onHover
694
+ };
695
+
696
+ var options_for_tree = {
697
+ onHover: Sortable.onEmptyHover,
698
+ overlap: options.overlap,
699
+ containment: options.containment,
700
+ hoverclass: options.hoverclass
701
+ };
702
+
703
+ // fix for gecko engine
704
+ Element.cleanWhitespace(element);
705
+
706
+ options.draggables = [];
707
+ options.droppables = [];
708
+
709
+ // drop on empty handling
710
+ if(options.dropOnEmpty || options.tree) {
711
+ Droppables.add(element, options_for_tree);
712
+ options.droppables.push(element);
713
+ }
714
+
715
+ (options.elements || this.findElements(element, options) || []).each( function(e,i) {
716
+ var handle = options.handles ? $(options.handles[i]) :
717
+ (options.handle ? $(e).select('.' + options.handle)[0] : e);
718
+ options.draggables.push(
719
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
720
+ Droppables.add(e, options_for_droppable);
721
+ if(options.tree) e.treeNode = element;
722
+ options.droppables.push(e);
723
+ });
724
+
725
+ if(options.tree) {
726
+ (Sortable.findTreeElements(element, options) || []).each( function(e) {
727
+ Droppables.add(e, options_for_tree);
728
+ e.treeNode = element;
729
+ options.droppables.push(e);
730
+ });
731
+ }
732
+
733
+ // keep reference
734
+ this.sortables[element.identify()] = options;
735
+
736
+ // for onupdate
737
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
738
+
739
+ },
740
+
741
+ // return all suitable-for-sortable elements in a guaranteed order
742
+ findElements: function(element, options) {
743
+ return Element.findChildren(
744
+ element, options.only, options.tree ? true : false, options.tag);
745
+ },
746
+
747
+ findTreeElements: function(element, options) {
748
+ return Element.findChildren(
749
+ element, options.only, options.tree ? true : false, options.treeTag);
750
+ },
751
+
752
+ onHover: function(element, dropon, overlap) {
753
+ if(Element.isParent(dropon, element)) return;
754
+
755
+ if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
756
+ return;
757
+ } else if(overlap>0.5) {
758
+ Sortable.mark(dropon, 'before');
759
+ if(dropon.previousSibling != element) {
760
+ var oldParentNode = element.parentNode;
761
+ element.style.visibility = "hidden"; // fix gecko rendering
762
+ dropon.parentNode.insertBefore(element, dropon);
763
+ if(dropon.parentNode!=oldParentNode)
764
+ Sortable.options(oldParentNode).onChange(element);
765
+ Sortable.options(dropon.parentNode).onChange(element);
766
+ }
767
+ } else {
768
+ Sortable.mark(dropon, 'after');
769
+ var nextElement = dropon.nextSibling || null;
770
+ if(nextElement != element) {
771
+ var oldParentNode = element.parentNode;
772
+ element.style.visibility = "hidden"; // fix gecko rendering
773
+ dropon.parentNode.insertBefore(element, nextElement);
774
+ if(dropon.parentNode!=oldParentNode)
775
+ Sortable.options(oldParentNode).onChange(element);
776
+ Sortable.options(dropon.parentNode).onChange(element);
777
+ }
778
+ }
779
+ },
780
+
781
+ onEmptyHover: function(element, dropon, overlap) {
782
+ var oldParentNode = element.parentNode;
783
+ var droponOptions = Sortable.options(dropon);
784
+
785
+ if(!Element.isParent(dropon, element)) {
786
+ var index;
787
+
788
+ var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
789
+ var child = null;
790
+
791
+ if(children) {
792
+ var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
793
+
794
+ for (index = 0; index < children.length; index += 1) {
795
+ if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
796
+ offset -= Element.offsetSize (children[index], droponOptions.overlap);
797
+ } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
798
+ child = index + 1 < children.length ? children[index + 1] : null;
799
+ break;
800
+ } else {
801
+ child = children[index];
802
+ break;
803
+ }
804
+ }
805
+ }
806
+
807
+ dropon.insertBefore(element, child);
808
+
809
+ Sortable.options(oldParentNode).onChange(element);
810
+ droponOptions.onChange(element);
811
+ }
812
+ },
813
+
814
+ unmark: function() {
815
+ if(Sortable._marker) Sortable._marker.hide();
816
+ },
817
+
818
+ mark: function(dropon, position) {
819
+ // mark on ghosting only
820
+ var sortable = Sortable.options(dropon.parentNode);
821
+ if(sortable && !sortable.ghosting) return;
822
+
823
+ if(!Sortable._marker) {
824
+ Sortable._marker =
825
+ ($('dropmarker') || Element.extend(document.createElement('DIV'))).
826
+ hide().addClassName('dropmarker').setStyle({position:'absolute'});
827
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
828
+ }
829
+ var offsets = dropon.cumulativeOffset();
830
+ Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
831
+
832
+ if(position=='after')
833
+ if(sortable.overlap == 'horizontal')
834
+ Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
835
+ else
836
+ Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
837
+
838
+ Sortable._marker.show();
839
+ },
840
+
841
+ _tree: function(element, options, parent) {
842
+ var children = Sortable.findElements(element, options) || [];
843
+
844
+ for (var i = 0; i < children.length; ++i) {
845
+ var match = children[i].id.match(options.format);
846
+
847
+ if (!match) continue;
848
+
849
+ var child = {
850
+ id: encodeURIComponent(match ? match[1] : null),
851
+ element: element,
852
+ parent: parent,
853
+ children: [],
854
+ position: parent.children.length,
855
+ container: $(children[i]).down(options.treeTag)
856
+ };
857
+
858
+ /* Get the element containing the children and recurse over it */
859
+ if (child.container)
860
+ this._tree(child.container, options, child);
861
+
862
+ parent.children.push (child);
863
+ }
864
+
865
+ return parent;
866
+ },
867
+
868
+ tree: function(element) {
869
+ element = $(element);
870
+ var sortableOptions = this.options(element);
871
+ var options = Object.extend({
872
+ tag: sortableOptions.tag,
873
+ treeTag: sortableOptions.treeTag,
874
+ only: sortableOptions.only,
875
+ name: element.id,
876
+ format: sortableOptions.format
877
+ }, arguments[1] || { });
878
+
879
+ var root = {
880
+ id: null,
881
+ parent: null,
882
+ children: [],
883
+ container: element,
884
+ position: 0
885
+ };
886
+
887
+ return Sortable._tree(element, options, root);
888
+ },
889
+
890
+ /* Construct a [i] index for a particular node */
891
+ _constructIndex: function(node) {
892
+ var index = '';
893
+ do {
894
+ if (node.id) index = '[' + node.position + ']' + index;
895
+ } while ((node = node.parent) != null);
896
+ return index;
897
+ },
898
+
899
+ sequence: function(element) {
900
+ element = $(element);
901
+ var options = Object.extend(this.options(element), arguments[1] || { });
902
+
903
+ return $(this.findElements(element, options) || []).map( function(item) {
904
+ return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
905
+ });
906
+ },
907
+
908
+ setSequence: function(element, new_sequence) {
909
+ element = $(element);
910
+ var options = Object.extend(this.options(element), arguments[2] || { });
911
+
912
+ var nodeMap = { };
913
+ this.findElements(element, options).each( function(n) {
914
+ if (n.id.match(options.format))
915
+ nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
916
+ n.parentNode.removeChild(n);
917
+ });
918
+
919
+ new_sequence.each(function(ident) {
920
+ var n = nodeMap[ident];
921
+ if (n) {
922
+ n[1].appendChild(n[0]);
923
+ delete nodeMap[ident];
924
+ }
925
+ });
926
+ },
927
+
928
+ serialize: function(element) {
929
+ element = $(element);
930
+ var options = Object.extend(Sortable.options(element), arguments[1] || { });
931
+ var name = encodeURIComponent(
932
+ (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
933
+
934
+ if (options.tree) {
935
+ return Sortable.tree(element, arguments[1]).children.map( function (item) {
936
+ return [name + Sortable._constructIndex(item) + "[id]=" +
937
+ encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
938
+ }).flatten().join('&');
939
+ } else {
940
+ return Sortable.sequence(element, arguments[1]).map( function(item) {
941
+ return name + "[]=" + encodeURIComponent(item);
942
+ }).join('&');
943
+ }
944
+ }
945
+ };
946
+
947
+ // Returns true if child is contained within element
948
+ Element.isParent = function(child, element) {
949
+ if (!child.parentNode || child == element) return false;
950
+ if (child.parentNode == element) return true;
951
+ return Element.isParent(child.parentNode, element);
952
+ };
953
+
954
+ Element.findChildren = function(element, only, recursive, tagName) {
955
+ if(!element.hasChildNodes()) return null;
956
+ tagName = tagName.toUpperCase();
957
+ if(only) only = [only].flatten();
958
+ var elements = [];
959
+ $A(element.childNodes).each( function(e) {
960
+ if(e.tagName && e.tagName.toUpperCase()==tagName &&
961
+ (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
962
+ elements.push(e);
963
+ if(recursive) {
964
+ var grandchildren = Element.findChildren(e, only, recursive, tagName);
965
+ if(grandchildren) elements.push(grandchildren);
966
+ }
967
+ });
968
+
969
+ return (elements.length>0 ? elements.flatten() : []);
970
+ };
971
+
972
+ Element.offsetSize = function (element, type) {
973
+ return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
974
+ };
js/mageho/sortproducts/effects.js ADDED
@@ -0,0 +1,1123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // script.aculo.us effects.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
2
+
3
+ // Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ // Contributors:
5
+ // Justin Palmer (http://encytemedia.com/)
6
+ // Mark Pilgrim (http://diveintomark.org/)
7
+ // Martin Bialasinki
8
+ //
9
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
10
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
11
+
12
+ // converts rgb() and #xxx to #xxxxxx format,
13
+ // returns self (or first argument) if not convertable
14
+ String.prototype.parseColor = function() {
15
+ var color = '#';
16
+ if (this.slice(0,4) == 'rgb(') {
17
+ var cols = this.slice(4,this.length-1).split(',');
18
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
19
+ } else {
20
+ if (this.slice(0,1) == '#') {
21
+ if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
22
+ if (this.length==7) color = this.toLowerCase();
23
+ }
24
+ }
25
+ return (color.length==7 ? color : (arguments[0] || this));
26
+ };
27
+
28
+ /*--------------------------------------------------------------------------*/
29
+
30
+ Element.collectTextNodes = function(element) {
31
+ return $A($(element).childNodes).collect( function(node) {
32
+ return (node.nodeType==3 ? node.nodeValue :
33
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
34
+ }).flatten().join('');
35
+ };
36
+
37
+ Element.collectTextNodesIgnoreClass = function(element, className) {
38
+ return $A($(element).childNodes).collect( function(node) {
39
+ return (node.nodeType==3 ? node.nodeValue :
40
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
41
+ Element.collectTextNodesIgnoreClass(node, className) : ''));
42
+ }).flatten().join('');
43
+ };
44
+
45
+ Element.setContentZoom = function(element, percent) {
46
+ element = $(element);
47
+ element.setStyle({fontSize: (percent/100) + 'em'});
48
+ if (Prototype.Browser.WebKit) window.scrollBy(0,0);
49
+ return element;
50
+ };
51
+
52
+ Element.getInlineOpacity = function(element){
53
+ return $(element).style.opacity || '';
54
+ };
55
+
56
+ Element.forceRerendering = function(element) {
57
+ try {
58
+ element = $(element);
59
+ var n = document.createTextNode(' ');
60
+ element.appendChild(n);
61
+ element.removeChild(n);
62
+ } catch(e) { }
63
+ };
64
+
65
+ /*--------------------------------------------------------------------------*/
66
+
67
+ var Effect = {
68
+ _elementDoesNotExistError: {
69
+ name: 'ElementDoesNotExistError',
70
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
71
+ },
72
+ Transitions: {
73
+ linear: Prototype.K,
74
+ sinoidal: function(pos) {
75
+ return (-Math.cos(pos*Math.PI)/2) + .5;
76
+ },
77
+ reverse: function(pos) {
78
+ return 1-pos;
79
+ },
80
+ flicker: function(pos) {
81
+ var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
82
+ return pos > 1 ? 1 : pos;
83
+ },
84
+ wobble: function(pos) {
85
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
86
+ },
87
+ pulse: function(pos, pulses) {
88
+ return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
89
+ },
90
+ spring: function(pos) {
91
+ return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
92
+ },
93
+ none: function(pos) {
94
+ return 0;
95
+ },
96
+ full: function(pos) {
97
+ return 1;
98
+ }
99
+ },
100
+ DefaultOptions: {
101
+ duration: 1.0, // seconds
102
+ fps: 100, // 100= assume 66fps max.
103
+ sync: false, // true for combining
104
+ from: 0.0,
105
+ to: 1.0,
106
+ delay: 0.0,
107
+ queue: 'parallel'
108
+ },
109
+ tagifyText: function(element) {
110
+ var tagifyStyle = 'position:relative';
111
+ if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
112
+
113
+ element = $(element);
114
+ $A(element.childNodes).each( function(child) {
115
+ if (child.nodeType==3) {
116
+ child.nodeValue.toArray().each( function(character) {
117
+ element.insertBefore(
118
+ new Element('span', {style: tagifyStyle}).update(
119
+ character == ' ' ? String.fromCharCode(160) : character),
120
+ child);
121
+ });
122
+ Element.remove(child);
123
+ }
124
+ });
125
+ },
126
+ multiple: function(element, effect) {
127
+ var elements;
128
+ if (((typeof element == 'object') ||
129
+ Object.isFunction(element)) &&
130
+ (element.length))
131
+ elements = element;
132
+ else
133
+ elements = $(element).childNodes;
134
+
135
+ var options = Object.extend({
136
+ speed: 0.1,
137
+ delay: 0.0
138
+ }, arguments[2] || { });
139
+ var masterDelay = options.delay;
140
+
141
+ $A(elements).each( function(element, index) {
142
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
143
+ });
144
+ },
145
+ PAIRS: {
146
+ 'slide': ['SlideDown','SlideUp'],
147
+ 'blind': ['BlindDown','BlindUp'],
148
+ 'appear': ['Appear','Fade']
149
+ },
150
+ toggle: function(element, effect, options) {
151
+ element = $(element);
152
+ effect = (effect || 'appear').toLowerCase();
153
+
154
+ return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
155
+ queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
156
+ }, options || {}));
157
+ }
158
+ };
159
+
160
+ Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
161
+
162
+ /* ------------- core effects ------------- */
163
+
164
+ Effect.ScopedQueue = Class.create(Enumerable, {
165
+ initialize: function() {
166
+ this.effects = [];
167
+ this.interval = null;
168
+ },
169
+ _each: function(iterator) {
170
+ this.effects._each(iterator);
171
+ },
172
+ add: function(effect) {
173
+ var timestamp = new Date().getTime();
174
+
175
+ var position = Object.isString(effect.options.queue) ?
176
+ effect.options.queue : effect.options.queue.position;
177
+
178
+ switch(position) {
179
+ case 'front':
180
+ // move unstarted effects after this effect
181
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
182
+ e.startOn += effect.finishOn;
183
+ e.finishOn += effect.finishOn;
184
+ });
185
+ break;
186
+ case 'with-last':
187
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
188
+ break;
189
+ case 'end':
190
+ // start effect after last queued effect has finished
191
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
192
+ break;
193
+ }
194
+
195
+ effect.startOn += timestamp;
196
+ effect.finishOn += timestamp;
197
+
198
+ if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
199
+ this.effects.push(effect);
200
+
201
+ if (!this.interval)
202
+ this.interval = setInterval(this.loop.bind(this), 15);
203
+ },
204
+ remove: function(effect) {
205
+ this.effects = this.effects.reject(function(e) { return e==effect });
206
+ if (this.effects.length == 0) {
207
+ clearInterval(this.interval);
208
+ this.interval = null;
209
+ }
210
+ },
211
+ loop: function() {
212
+ var timePos = new Date().getTime();
213
+ for(var i=0, len=this.effects.length;i<len;i++)
214
+ this.effects[i] && this.effects[i].loop(timePos);
215
+ }
216
+ });
217
+
218
+ Effect.Queues = {
219
+ instances: $H(),
220
+ get: function(queueName) {
221
+ if (!Object.isString(queueName)) return queueName;
222
+
223
+ return this.instances.get(queueName) ||
224
+ this.instances.set(queueName, new Effect.ScopedQueue());
225
+ }
226
+ };
227
+ Effect.Queue = Effect.Queues.get('global');
228
+
229
+ Effect.Base = Class.create({
230
+ position: null,
231
+ start: function(options) {
232
+ if (options && options.transition === false) options.transition = Effect.Transitions.linear;
233
+ this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
234
+ this.currentFrame = 0;
235
+ this.state = 'idle';
236
+ this.startOn = this.options.delay*1000;
237
+ this.finishOn = this.startOn+(this.options.duration*1000);
238
+ this.fromToDelta = this.options.to-this.options.from;
239
+ this.totalTime = this.finishOn-this.startOn;
240
+ this.totalFrames = this.options.fps*this.options.duration;
241
+
242
+ this.render = (function() {
243
+ function dispatch(effect, eventName) {
244
+ if (effect.options[eventName + 'Internal'])
245
+ effect.options[eventName + 'Internal'](effect);
246
+ if (effect.options[eventName])
247
+ effect.options[eventName](effect);
248
+ }
249
+
250
+ return function(pos) {
251
+ if (this.state === "idle") {
252
+ this.state = "running";
253
+ dispatch(this, 'beforeSetup');
254
+ if (this.setup) this.setup();
255
+ dispatch(this, 'afterSetup');
256
+ }
257
+ if (this.state === "running") {
258
+ pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
259
+ this.position = pos;
260
+ dispatch(this, 'beforeUpdate');
261
+ if (this.update) this.update(pos);
262
+ dispatch(this, 'afterUpdate');
263
+ }
264
+ };
265
+ })();
266
+
267
+ this.event('beforeStart');
268
+ if (!this.options.sync)
269
+ Effect.Queues.get(Object.isString(this.options.queue) ?
270
+ 'global' : this.options.queue.scope).add(this);
271
+ },
272
+ loop: function(timePos) {
273
+ if (timePos >= this.startOn) {
274
+ if (timePos >= this.finishOn) {
275
+ this.render(1.0);
276
+ this.cancel();
277
+ this.event('beforeFinish');
278
+ if (this.finish) this.finish();
279
+ this.event('afterFinish');
280
+ return;
281
+ }
282
+ var pos = (timePos - this.startOn) / this.totalTime,
283
+ frame = (pos * this.totalFrames).round();
284
+ if (frame > this.currentFrame) {
285
+ this.render(pos);
286
+ this.currentFrame = frame;
287
+ }
288
+ }
289
+ },
290
+ cancel: function() {
291
+ if (!this.options.sync)
292
+ Effect.Queues.get(Object.isString(this.options.queue) ?
293
+ 'global' : this.options.queue.scope).remove(this);
294
+ this.state = 'finished';
295
+ },
296
+ event: function(eventName) {
297
+ if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
298
+ if (this.options[eventName]) this.options[eventName](this);
299
+ },
300
+ inspect: function() {
301
+ var data = $H();
302
+ for(property in this)
303
+ if (!Object.isFunction(this[property])) data.set(property, this[property]);
304
+ return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
305
+ }
306
+ });
307
+
308
+ Effect.Parallel = Class.create(Effect.Base, {
309
+ initialize: function(effects) {
310
+ this.effects = effects || [];
311
+ this.start(arguments[1]);
312
+ },
313
+ update: function(position) {
314
+ this.effects.invoke('render', position);
315
+ },
316
+ finish: function(position) {
317
+ this.effects.each( function(effect) {
318
+ effect.render(1.0);
319
+ effect.cancel();
320
+ effect.event('beforeFinish');
321
+ if (effect.finish) effect.finish(position);
322
+ effect.event('afterFinish');
323
+ });
324
+ }
325
+ });
326
+
327
+ Effect.Tween = Class.create(Effect.Base, {
328
+ initialize: function(object, from, to) {
329
+ object = Object.isString(object) ? $(object) : object;
330
+ var args = $A(arguments), method = args.last(),
331
+ options = args.length == 5 ? args[3] : null;
332
+ this.method = Object.isFunction(method) ? method.bind(object) :
333
+ Object.isFunction(object[method]) ? object[method].bind(object) :
334
+ function(value) { object[method] = value };
335
+ this.start(Object.extend({ from: from, to: to }, options || { }));
336
+ },
337
+ update: function(position) {
338
+ this.method(position);
339
+ }
340
+ });
341
+
342
+ Effect.Event = Class.create(Effect.Base, {
343
+ initialize: function() {
344
+ this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
345
+ },
346
+ update: Prototype.emptyFunction
347
+ });
348
+
349
+ Effect.Opacity = Class.create(Effect.Base, {
350
+ initialize: function(element) {
351
+ this.element = $(element);
352
+ if (!this.element) throw(Effect._elementDoesNotExistError);
353
+ // make this work on IE on elements without 'layout'
354
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
355
+ this.element.setStyle({zoom: 1});
356
+ var options = Object.extend({
357
+ from: this.element.getOpacity() || 0.0,
358
+ to: 1.0
359
+ }, arguments[1] || { });
360
+ this.start(options);
361
+ },
362
+ update: function(position) {
363
+ this.element.setOpacity(position);
364
+ }
365
+ });
366
+
367
+ Effect.Move = Class.create(Effect.Base, {
368
+ initialize: function(element) {
369
+ this.element = $(element);
370
+ if (!this.element) throw(Effect._elementDoesNotExistError);
371
+ var options = Object.extend({
372
+ x: 0,
373
+ y: 0,
374
+ mode: 'relative'
375
+ }, arguments[1] || { });
376
+ this.start(options);
377
+ },
378
+ setup: function() {
379
+ this.element.makePositioned();
380
+ this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
381
+ this.originalTop = parseFloat(this.element.getStyle('top') || '0');
382
+ if (this.options.mode == 'absolute') {
383
+ this.options.x = this.options.x - this.originalLeft;
384
+ this.options.y = this.options.y - this.originalTop;
385
+ }
386
+ },
387
+ update: function(position) {
388
+ this.element.setStyle({
389
+ left: (this.options.x * position + this.originalLeft).round() + 'px',
390
+ top: (this.options.y * position + this.originalTop).round() + 'px'
391
+ });
392
+ }
393
+ });
394
+
395
+ // for backwards compatibility
396
+ Effect.MoveBy = function(element, toTop, toLeft) {
397
+ return new Effect.Move(element,
398
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
399
+ };
400
+
401
+ Effect.Scale = Class.create(Effect.Base, {
402
+ initialize: function(element, percent) {
403
+ this.element = $(element);
404
+ if (!this.element) throw(Effect._elementDoesNotExistError);
405
+ var options = Object.extend({
406
+ scaleX: true,
407
+ scaleY: true,
408
+ scaleContent: true,
409
+ scaleFromCenter: false,
410
+ scaleMode: 'box', // 'box' or 'contents' or { } with provided values
411
+ scaleFrom: 100.0,
412
+ scaleTo: percent
413
+ }, arguments[2] || { });
414
+ this.start(options);
415
+ },
416
+ setup: function() {
417
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
418
+ this.elementPositioning = this.element.getStyle('position');
419
+
420
+ this.originalStyle = { };
421
+ ['top','left','width','height','fontSize'].each( function(k) {
422
+ this.originalStyle[k] = this.element.style[k];
423
+ }.bind(this));
424
+
425
+ this.originalTop = this.element.offsetTop;
426
+ this.originalLeft = this.element.offsetLeft;
427
+
428
+ var fontSize = this.element.getStyle('font-size') || '100%';
429
+ ['em','px','%','pt'].each( function(fontSizeType) {
430
+ if (fontSize.indexOf(fontSizeType)>0) {
431
+ this.fontSize = parseFloat(fontSize);
432
+ this.fontSizeType = fontSizeType;
433
+ }
434
+ }.bind(this));
435
+
436
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
437
+
438
+ this.dims = null;
439
+ if (this.options.scaleMode=='box')
440
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
441
+ if (/^content/.test(this.options.scaleMode))
442
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
443
+ if (!this.dims)
444
+ this.dims = [this.options.scaleMode.originalHeight,
445
+ this.options.scaleMode.originalWidth];
446
+ },
447
+ update: function(position) {
448
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
449
+ if (this.options.scaleContent && this.fontSize)
450
+ this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
451
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
452
+ },
453
+ finish: function(position) {
454
+ if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
455
+ },
456
+ setDimensions: function(height, width) {
457
+ var d = { };
458
+ if (this.options.scaleX) d.width = width.round() + 'px';
459
+ if (this.options.scaleY) d.height = height.round() + 'px';
460
+ if (this.options.scaleFromCenter) {
461
+ var topd = (height - this.dims[0])/2;
462
+ var leftd = (width - this.dims[1])/2;
463
+ if (this.elementPositioning == 'absolute') {
464
+ if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
465
+ if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
466
+ } else {
467
+ if (this.options.scaleY) d.top = -topd + 'px';
468
+ if (this.options.scaleX) d.left = -leftd + 'px';
469
+ }
470
+ }
471
+ this.element.setStyle(d);
472
+ }
473
+ });
474
+
475
+ Effect.Highlight = Class.create(Effect.Base, {
476
+ initialize: function(element) {
477
+ this.element = $(element);
478
+ if (!this.element) throw(Effect._elementDoesNotExistError);
479
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
480
+ this.start(options);
481
+ },
482
+ setup: function() {
483
+ // Prevent executing on elements not in the layout flow
484
+ if (this.element.getStyle('display')=='none') { this.cancel(); return; }
485
+ // Disable background image during the effect
486
+ this.oldStyle = { };
487
+ if (!this.options.keepBackgroundImage) {
488
+ this.oldStyle.backgroundImage = this.element.getStyle('background-image');
489
+ this.element.setStyle({backgroundImage: 'none'});
490
+ }
491
+ if (!this.options.endcolor)
492
+ this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
493
+ if (!this.options.restorecolor)
494
+ this.options.restorecolor = this.element.getStyle('background-color');
495
+ // init color calculations
496
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
497
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
498
+ },
499
+ update: function(position) {
500
+ this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
501
+ return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
502
+ },
503
+ finish: function() {
504
+ this.element.setStyle(Object.extend(this.oldStyle, {
505
+ backgroundColor: this.options.restorecolor
506
+ }));
507
+ }
508
+ });
509
+
510
+ Effect.ScrollTo = function(element) {
511
+ var options = arguments[1] || { },
512
+ scrollOffsets = document.viewport.getScrollOffsets(),
513
+ elementOffsets = $(element).cumulativeOffset();
514
+
515
+ if (options.offset) elementOffsets[1] += options.offset;
516
+
517
+ return new Effect.Tween(null,
518
+ scrollOffsets.top,
519
+ elementOffsets[1],
520
+ options,
521
+ function(p){ scrollTo(scrollOffsets.left, p.round()); }
522
+ );
523
+ };
524
+
525
+ /* ------------- combination effects ------------- */
526
+
527
+ Effect.Fade = function(element) {
528
+ element = $(element);
529
+ var oldOpacity = element.getInlineOpacity();
530
+ var options = Object.extend({
531
+ from: element.getOpacity() || 1.0,
532
+ to: 0.0,
533
+ afterFinishInternal: function(effect) {
534
+ if (effect.options.to!=0) return;
535
+ effect.element.hide().setStyle({opacity: oldOpacity});
536
+ }
537
+ }, arguments[1] || { });
538
+ return new Effect.Opacity(element,options);
539
+ };
540
+
541
+ Effect.Appear = function(element) {
542
+ element = $(element);
543
+ var options = Object.extend({
544
+ from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
545
+ to: 1.0,
546
+ // force Safari to render floated elements properly
547
+ afterFinishInternal: function(effect) {
548
+ effect.element.forceRerendering();
549
+ },
550
+ beforeSetup: function(effect) {
551
+ effect.element.setOpacity(effect.options.from).show();
552
+ }}, arguments[1] || { });
553
+ return new Effect.Opacity(element,options);
554
+ };
555
+
556
+ Effect.Puff = function(element) {
557
+ element = $(element);
558
+ var oldStyle = {
559
+ opacity: element.getInlineOpacity(),
560
+ position: element.getStyle('position'),
561
+ top: element.style.top,
562
+ left: element.style.left,
563
+ width: element.style.width,
564
+ height: element.style.height
565
+ };
566
+ return new Effect.Parallel(
567
+ [ new Effect.Scale(element, 200,
568
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
569
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
570
+ Object.extend({ duration: 1.0,
571
+ beforeSetupInternal: function(effect) {
572
+ Position.absolutize(effect.effects[0].element);
573
+ },
574
+ afterFinishInternal: function(effect) {
575
+ effect.effects[0].element.hide().setStyle(oldStyle); }
576
+ }, arguments[1] || { })
577
+ );
578
+ };
579
+
580
+ Effect.BlindUp = function(element) {
581
+ element = $(element);
582
+ element.makeClipping();
583
+ return new Effect.Scale(element, 0,
584
+ Object.extend({ scaleContent: false,
585
+ scaleX: false,
586
+ restoreAfterFinish: true,
587
+ afterFinishInternal: function(effect) {
588
+ effect.element.hide().undoClipping();
589
+ }
590
+ }, arguments[1] || { })
591
+ );
592
+ };
593
+
594
+ Effect.BlindDown = function(element) {
595
+ element = $(element);
596
+ var elementDimensions = element.getDimensions();
597
+ return new Effect.Scale(element, 100, Object.extend({
598
+ scaleContent: false,
599
+ scaleX: false,
600
+ scaleFrom: 0,
601
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
602
+ restoreAfterFinish: true,
603
+ afterSetup: function(effect) {
604
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
605
+ },
606
+ afterFinishInternal: function(effect) {
607
+ effect.element.undoClipping();
608
+ }
609
+ }, arguments[1] || { }));
610
+ };
611
+
612
+ Effect.SwitchOff = function(element) {
613
+ element = $(element);
614
+ var oldOpacity = element.getInlineOpacity();
615
+ return new Effect.Appear(element, Object.extend({
616
+ duration: 0.4,
617
+ from: 0,
618
+ transition: Effect.Transitions.flicker,
619
+ afterFinishInternal: function(effect) {
620
+ new Effect.Scale(effect.element, 1, {
621
+ duration: 0.3, scaleFromCenter: true,
622
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
623
+ beforeSetup: function(effect) {
624
+ effect.element.makePositioned().makeClipping();
625
+ },
626
+ afterFinishInternal: function(effect) {
627
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
628
+ }
629
+ });
630
+ }
631
+ }, arguments[1] || { }));
632
+ };
633
+
634
+ Effect.DropOut = function(element) {
635
+ element = $(element);
636
+ var oldStyle = {
637
+ top: element.getStyle('top'),
638
+ left: element.getStyle('left'),
639
+ opacity: element.getInlineOpacity() };
640
+ return new Effect.Parallel(
641
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
642
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
643
+ Object.extend(
644
+ { duration: 0.5,
645
+ beforeSetup: function(effect) {
646
+ effect.effects[0].element.makePositioned();
647
+ },
648
+ afterFinishInternal: function(effect) {
649
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
650
+ }
651
+ }, arguments[1] || { }));
652
+ };
653
+
654
+ Effect.Shake = function(element) {
655
+ element = $(element);
656
+ var options = Object.extend({
657
+ distance: 20,
658
+ duration: 0.5
659
+ }, arguments[1] || {});
660
+ var distance = parseFloat(options.distance);
661
+ var split = parseFloat(options.duration) / 10.0;
662
+ var oldStyle = {
663
+ top: element.getStyle('top'),
664
+ left: element.getStyle('left') };
665
+ return new Effect.Move(element,
666
+ { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
667
+ new Effect.Move(effect.element,
668
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
669
+ new Effect.Move(effect.element,
670
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
671
+ new Effect.Move(effect.element,
672
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
673
+ new Effect.Move(effect.element,
674
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
675
+ new Effect.Move(effect.element,
676
+ { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
677
+ effect.element.undoPositioned().setStyle(oldStyle);
678
+ }}); }}); }}); }}); }}); }});
679
+ };
680
+
681
+ Effect.SlideDown = function(element) {
682
+ element = $(element).cleanWhitespace();
683
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
684
+ var oldInnerBottom = element.down().getStyle('bottom');
685
+ var elementDimensions = element.getDimensions();
686
+ return new Effect.Scale(element, 100, Object.extend({
687
+ scaleContent: false,
688
+ scaleX: false,
689
+ scaleFrom: window.opera ? 0 : 1,
690
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
691
+ restoreAfterFinish: true,
692
+ afterSetup: function(effect) {
693
+ effect.element.makePositioned();
694
+ effect.element.down().makePositioned();
695
+ if (window.opera) effect.element.setStyle({top: ''});
696
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
697
+ },
698
+ afterUpdateInternal: function(effect) {
699
+ effect.element.down().setStyle({bottom:
700
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
701
+ },
702
+ afterFinishInternal: function(effect) {
703
+ effect.element.undoClipping().undoPositioned();
704
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
705
+ }, arguments[1] || { })
706
+ );
707
+ };
708
+
709
+ Effect.SlideUp = function(element) {
710
+ element = $(element).cleanWhitespace();
711
+ var oldInnerBottom = element.down().getStyle('bottom');
712
+ var elementDimensions = element.getDimensions();
713
+ return new Effect.Scale(element, window.opera ? 0 : 1,
714
+ Object.extend({ scaleContent: false,
715
+ scaleX: false,
716
+ scaleMode: 'box',
717
+ scaleFrom: 100,
718
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
719
+ restoreAfterFinish: true,
720
+ afterSetup: function(effect) {
721
+ effect.element.makePositioned();
722
+ effect.element.down().makePositioned();
723
+ if (window.opera) effect.element.setStyle({top: ''});
724
+ effect.element.makeClipping().show();
725
+ },
726
+ afterUpdateInternal: function(effect) {
727
+ effect.element.down().setStyle({bottom:
728
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
729
+ },
730
+ afterFinishInternal: function(effect) {
731
+ effect.element.hide().undoClipping().undoPositioned();
732
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
733
+ }
734
+ }, arguments[1] || { })
735
+ );
736
+ };
737
+
738
+ // Bug in opera makes the TD containing this element expand for a instance after finish
739
+ Effect.Squish = function(element) {
740
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
741
+ restoreAfterFinish: true,
742
+ beforeSetup: function(effect) {
743
+ effect.element.makeClipping();
744
+ },
745
+ afterFinishInternal: function(effect) {
746
+ effect.element.hide().undoClipping();
747
+ }
748
+ });
749
+ };
750
+
751
+ Effect.Grow = function(element) {
752
+ element = $(element);
753
+ var options = Object.extend({
754
+ direction: 'center',
755
+ moveTransition: Effect.Transitions.sinoidal,
756
+ scaleTransition: Effect.Transitions.sinoidal,
757
+ opacityTransition: Effect.Transitions.full
758
+ }, arguments[1] || { });
759
+ var oldStyle = {
760
+ top: element.style.top,
761
+ left: element.style.left,
762
+ height: element.style.height,
763
+ width: element.style.width,
764
+ opacity: element.getInlineOpacity() };
765
+
766
+ var dims = element.getDimensions();
767
+ var initialMoveX, initialMoveY;
768
+ var moveX, moveY;
769
+
770
+ switch (options.direction) {
771
+ case 'top-left':
772
+ initialMoveX = initialMoveY = moveX = moveY = 0;
773
+ break;
774
+ case 'top-right':
775
+ initialMoveX = dims.width;
776
+ initialMoveY = moveY = 0;
777
+ moveX = -dims.width;
778
+ break;
779
+ case 'bottom-left':
780
+ initialMoveX = moveX = 0;
781
+ initialMoveY = dims.height;
782
+ moveY = -dims.height;
783
+ break;
784
+ case 'bottom-right':
785
+ initialMoveX = dims.width;
786
+ initialMoveY = dims.height;
787
+ moveX = -dims.width;
788
+ moveY = -dims.height;
789
+ break;
790
+ case 'center':
791
+ initialMoveX = dims.width / 2;
792
+ initialMoveY = dims.height / 2;
793
+ moveX = -dims.width / 2;
794
+ moveY = -dims.height / 2;
795
+ break;
796
+ }
797
+
798
+ return new Effect.Move(element, {
799
+ x: initialMoveX,
800
+ y: initialMoveY,
801
+ duration: 0.01,
802
+ beforeSetup: function(effect) {
803
+ effect.element.hide().makeClipping().makePositioned();
804
+ },
805
+ afterFinishInternal: function(effect) {
806
+ new Effect.Parallel(
807
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
808
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
809
+ new Effect.Scale(effect.element, 100, {
810
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
811
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
812
+ ], Object.extend({
813
+ beforeSetup: function(effect) {
814
+ effect.effects[0].element.setStyle({height: '0px'}).show();
815
+ },
816
+ afterFinishInternal: function(effect) {
817
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
818
+ }
819
+ }, options)
820
+ );
821
+ }
822
+ });
823
+ };
824
+
825
+ Effect.Shrink = function(element) {
826
+ element = $(element);
827
+ var options = Object.extend({
828
+ direction: 'center',
829
+ moveTransition: Effect.Transitions.sinoidal,
830
+ scaleTransition: Effect.Transitions.sinoidal,
831
+ opacityTransition: Effect.Transitions.none
832
+ }, arguments[1] || { });
833
+ var oldStyle = {
834
+ top: element.style.top,
835
+ left: element.style.left,
836
+ height: element.style.height,
837
+ width: element.style.width,
838
+ opacity: element.getInlineOpacity() };
839
+
840
+ var dims = element.getDimensions();
841
+ var moveX, moveY;
842
+
843
+ switch (options.direction) {
844
+ case 'top-left':
845
+ moveX = moveY = 0;
846
+ break;
847
+ case 'top-right':
848
+ moveX = dims.width;
849
+ moveY = 0;
850
+ break;
851
+ case 'bottom-left':
852
+ moveX = 0;
853
+ moveY = dims.height;
854
+ break;
855
+ case 'bottom-right':
856
+ moveX = dims.width;
857
+ moveY = dims.height;
858
+ break;
859
+ case 'center':
860
+ moveX = dims.width / 2;
861
+ moveY = dims.height / 2;
862
+ break;
863
+ }
864
+
865
+ return new Effect.Parallel(
866
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
867
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
868
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
869
+ ], Object.extend({
870
+ beforeStartInternal: function(effect) {
871
+ effect.effects[0].element.makePositioned().makeClipping();
872
+ },
873
+ afterFinishInternal: function(effect) {
874
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
875
+ }, options)
876
+ );
877
+ };
878
+
879
+ Effect.Pulsate = function(element) {
880
+ element = $(element);
881
+ var options = arguments[1] || { },
882
+ oldOpacity = element.getInlineOpacity(),
883
+ transition = options.transition || Effect.Transitions.linear,
884
+ reverser = function(pos){
885
+ return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
886
+ };
887
+
888
+ return new Effect.Opacity(element,
889
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
890
+ afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
891
+ }, options), {transition: reverser}));
892
+ };
893
+
894
+ Effect.Fold = function(element) {
895
+ element = $(element);
896
+ var oldStyle = {
897
+ top: element.style.top,
898
+ left: element.style.left,
899
+ width: element.style.width,
900
+ height: element.style.height };
901
+ element.makeClipping();
902
+ return new Effect.Scale(element, 5, Object.extend({
903
+ scaleContent: false,
904
+ scaleX: false,
905
+ afterFinishInternal: function(effect) {
906
+ new Effect.Scale(element, 1, {
907
+ scaleContent: false,
908
+ scaleY: false,
909
+ afterFinishInternal: function(effect) {
910
+ effect.element.hide().undoClipping().setStyle(oldStyle);
911
+ } });
912
+ }}, arguments[1] || { }));
913
+ };
914
+
915
+ Effect.Morph = Class.create(Effect.Base, {
916
+ initialize: function(element) {
917
+ this.element = $(element);
918
+ if (!this.element) throw(Effect._elementDoesNotExistError);
919
+ var options = Object.extend({
920
+ style: { }
921
+ }, arguments[1] || { });
922
+
923
+ if (!Object.isString(options.style)) this.style = $H(options.style);
924
+ else {
925
+ if (options.style.include(':'))
926
+ this.style = options.style.parseStyle();
927
+ else {
928
+ this.element.addClassName(options.style);
929
+ this.style = $H(this.element.getStyles());
930
+ this.element.removeClassName(options.style);
931
+ var css = this.element.getStyles();
932
+ this.style = this.style.reject(function(style) {
933
+ return style.value == css[style.key];
934
+ });
935
+ options.afterFinishInternal = function(effect) {
936
+ effect.element.addClassName(effect.options.style);
937
+ effect.transforms.each(function(transform) {
938
+ effect.element.style[transform.style] = '';
939
+ });
940
+ };
941
+ }
942
+ }
943
+ this.start(options);
944
+ },
945
+
946
+ setup: function(){
947
+ function parseColor(color){
948
+ if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
949
+ color = color.parseColor();
950
+ return $R(0,2).map(function(i){
951
+ return parseInt( color.slice(i*2+1,i*2+3), 16 );
952
+ });
953
+ }
954
+ this.transforms = this.style.map(function(pair){
955
+ var property = pair[0], value = pair[1], unit = null;
956
+
957
+ if (value.parseColor('#zzzzzz') != '#zzzzzz') {
958
+ value = value.parseColor();
959
+ unit = 'color';
960
+ } else if (property == 'opacity') {
961
+ value = parseFloat(value);
962
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
963
+ this.element.setStyle({zoom: 1});
964
+ } else if (Element.CSS_LENGTH.test(value)) {
965
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
966
+ value = parseFloat(components[1]);
967
+ unit = (components.length == 3) ? components[2] : null;
968
+ }
969
+
970
+ var originalValue = this.element.getStyle(property);
971
+ return {
972
+ style: property.camelize(),
973
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
974
+ targetValue: unit=='color' ? parseColor(value) : value,
975
+ unit: unit
976
+ };
977
+ }.bind(this)).reject(function(transform){
978
+ return (
979
+ (transform.originalValue == transform.targetValue) ||
980
+ (
981
+ transform.unit != 'color' &&
982
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
983
+ )
984
+ );
985
+ });
986
+ },
987
+ update: function(position) {
988
+ var style = { }, transform, i = this.transforms.length;
989
+ while(i--)
990
+ style[(transform = this.transforms[i]).style] =
991
+ transform.unit=='color' ? '#'+
992
+ (Math.round(transform.originalValue[0]+
993
+ (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
994
+ (Math.round(transform.originalValue[1]+
995
+ (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
996
+ (Math.round(transform.originalValue[2]+
997
+ (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
998
+ (transform.originalValue +
999
+ (transform.targetValue - transform.originalValue) * position).toFixed(3) +
1000
+ (transform.unit === null ? '' : transform.unit);
1001
+ this.element.setStyle(style, true);
1002
+ }
1003
+ });
1004
+
1005
+ Effect.Transform = Class.create({
1006
+ initialize: function(tracks){
1007
+ this.tracks = [];
1008
+ this.options = arguments[1] || { };
1009
+ this.addTracks(tracks);
1010
+ },
1011
+ addTracks: function(tracks){
1012
+ tracks.each(function(track){
1013
+ track = $H(track);
1014
+ var data = track.values().first();
1015
+ this.tracks.push($H({
1016
+ ids: track.keys().first(),
1017
+ effect: Effect.Morph,
1018
+ options: { style: data }
1019
+ }));
1020
+ }.bind(this));
1021
+ return this;
1022
+ },
1023
+ play: function(){
1024
+ return new Effect.Parallel(
1025
+ this.tracks.map(function(track){
1026
+ var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
1027
+ var elements = [$(ids) || $$(ids)].flatten();
1028
+ return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
1029
+ }).flatten(),
1030
+ this.options
1031
+ );
1032
+ }
1033
+ });
1034
+
1035
+ Element.CSS_PROPERTIES = $w(
1036
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
1037
+ 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
1038
+ 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
1039
+ 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
1040
+ 'fontSize fontWeight height left letterSpacing lineHeight ' +
1041
+ 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
1042
+ 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
1043
+ 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
1044
+ 'right textIndent top width wordSpacing zIndex');
1045
+
1046
+ Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1047
+
1048
+ String.__parseStyleElement = document.createElement('div');
1049
+ String.prototype.parseStyle = function(){
1050
+ var style, styleRules = $H();
1051
+ if (Prototype.Browser.WebKit)
1052
+ style = new Element('div',{style:this}).style;
1053
+ else {
1054
+ String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
1055
+ style = String.__parseStyleElement.childNodes[0].style;
1056
+ }
1057
+
1058
+ Element.CSS_PROPERTIES.each(function(property){
1059
+ if (style[property]) styleRules.set(property, style[property]);
1060
+ });
1061
+
1062
+ if (Prototype.Browser.IE && this.include('opacity'))
1063
+ styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
1064
+
1065
+ return styleRules;
1066
+ };
1067
+
1068
+ if (document.defaultView && document.defaultView.getComputedStyle) {
1069
+ Element.getStyles = function(element) {
1070
+ var css = document.defaultView.getComputedStyle($(element), null);
1071
+ return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
1072
+ styles[property] = css[property];
1073
+ return styles;
1074
+ });
1075
+ };
1076
+ } else {
1077
+ Element.getStyles = function(element) {
1078
+ element = $(element);
1079
+ var css = element.currentStyle, styles;
1080
+ styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
1081
+ results[property] = css[property];
1082
+ return results;
1083
+ });
1084
+ if (!styles.opacity) styles.opacity = element.getOpacity();
1085
+ return styles;
1086
+ };
1087
+ }
1088
+
1089
+ Effect.Methods = {
1090
+ morph: function(element, style) {
1091
+ element = $(element);
1092
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
1093
+ return element;
1094
+ },
1095
+ visualEffect: function(element, effect, options) {
1096
+ element = $(element);
1097
+ var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
1098
+ new Effect[klass](element, options);
1099
+ return element;
1100
+ },
1101
+ highlight: function(element, options) {
1102
+ element = $(element);
1103
+ new Effect.Highlight(element, options);
1104
+ return element;
1105
+ }
1106
+ };
1107
+
1108
+ $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
1109
+ 'pulsate shake puff squish switchOff dropOut').each(
1110
+ function(effect) {
1111
+ Effect.Methods[effect] = function(element, options){
1112
+ element = $(element);
1113
+ Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
1114
+ return element;
1115
+ };
1116
+ }
1117
+ );
1118
+
1119
+ $w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
1120
+ function(f) { Effect.Methods[f] = Element[f]; }
1121
+ );
1122
+
1123
+ Element.addMethods(Effect.Methods);
js/mageho/sortproducts/excanvas.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright 2006 Google Inc.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ document.createElement("canvas").getContext||(function(){var s=Math,j=s.round,F=s.sin,G=s.cos,V=s.abs,W=s.sqrt,k=10,v=k/2;function X(){return this.context_||(this.context_=new H(this))}var L=Array.prototype.slice;function Y(b,a){var c=L.call(arguments,2);return function(){return b.apply(a,c.concat(L.call(arguments)))}}var M={init:function(b){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var a=b||document;a.createElement("canvas");a.attachEvent("onreadystatechange",Y(this.init_,this,a))}},init_:function(b){b.namespaces.g_vml_||
15
+ b.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");b.namespaces.g_o_||b.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");if(!b.styleSheets.ex_canvas_){var a=b.createStyleSheet();a.owningElement.id="ex_canvas_";a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}g_o_\\:*{behavior:url(#default#VML)}"}var c=b.getElementsByTagName("canvas"),d=0;for(;d<c.length;d++)this.initElement(c[d])},
16
+ initElement:function(b){if(!b.getContext){b.getContext=X;b.innerHTML="";b.attachEvent("onpropertychange",Z);b.attachEvent("onresize",$);var a=b.attributes;if(a.width&&a.width.specified)b.style.width=a.width.nodeValue+"px";else b.width=b.clientWidth;if(a.height&&a.height.specified)b.style.height=a.height.nodeValue+"px";else b.height=b.clientHeight}return b}};function Z(b){var a=b.srcElement;switch(b.propertyName){case "width":a.style.width=a.attributes.width.nodeValue+"px";a.getContext().clearRect();
17
+ break;case "height":a.style.height=a.attributes.height.nodeValue+"px";a.getContext().clearRect();break}}function $(b){var a=b.srcElement;if(a.firstChild){a.firstChild.style.width=a.clientWidth+"px";a.firstChild.style.height=a.clientHeight+"px"}}M.init();var N=[],B=0;for(;B<16;B++){var C=0;for(;C<16;C++)N[B*16+C]=B.toString(16)+C.toString(16)}function I(){return[[1,0,0],[0,1,0],[0,0,1]]}function y(b,a){var c=I(),d=0;for(;d<3;d++){var f=0;for(;f<3;f++){var h=0,g=0;for(;g<3;g++)h+=b[d][g]*a[g][f];c[d][f]=
18
+ h}}return c}function O(b,a){a.fillStyle=b.fillStyle;a.lineCap=b.lineCap;a.lineJoin=b.lineJoin;a.lineWidth=b.lineWidth;a.miterLimit=b.miterLimit;a.shadowBlur=b.shadowBlur;a.shadowColor=b.shadowColor;a.shadowOffsetX=b.shadowOffsetX;a.shadowOffsetY=b.shadowOffsetY;a.strokeStyle=b.strokeStyle;a.globalAlpha=b.globalAlpha;a.arcScaleX_=b.arcScaleX_;a.arcScaleY_=b.arcScaleY_;a.lineScale_=b.lineScale_}function P(b){var a,c=1;b=String(b);if(b.substring(0,3)=="rgb"){var d=b.indexOf("(",3),f=b.indexOf(")",d+
19
+ 1),h=b.substring(d+1,f).split(",");a="#";var g=0;for(;g<3;g++)a+=N[Number(h[g])];if(h.length==4&&b.substr(3,1)=="a")c=h[3]}else a=b;return{color:a,alpha:c}}function aa(b){switch(b){case "butt":return"flat";case "round":return"round";case "square":default:return"square"}}function H(b){this.m_=I();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.fillStyle=this.strokeStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=k*1;this.globalAlpha=1;this.canvas=b;
20
+ var a=b.ownerDocument.createElement("div");a.style.width=b.clientWidth+"px";a.style.height=b.clientHeight+"px";a.style.position="absolute";b.appendChild(a);this.element_=a;this.lineScale_=this.arcScaleY_=this.arcScaleX_=1}var i=H.prototype;i.clearRect=function(){this.element_.innerHTML=""};i.beginPath=function(){this.currentPath_=[]};i.moveTo=function(b,a){var c=this.getCoords_(b,a);this.currentPath_.push({type:"moveTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};
21
+ i.lineTo=function(b,a){var c=this.getCoords_(b,a);this.currentPath_.push({type:"lineTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};i.bezierCurveTo=function(b,a,c,d,f,h){var g=this.getCoords_(f,h),l=this.getCoords_(b,a),e=this.getCoords_(c,d);Q(this,l,e,g)};function Q(b,a,c,d){b.currentPath_.push({type:"bezierCurveTo",cp1x:a.x,cp1y:a.y,cp2x:c.x,cp2y:c.y,x:d.x,y:d.y});b.currentX_=d.x;b.currentY_=d.y}i.quadraticCurveTo=function(b,a,c,d){var f=this.getCoords_(b,a),h=this.getCoords_(c,d),g={x:this.currentX_+
22
+ 0.6666666666666666*(f.x-this.currentX_),y:this.currentY_+0.6666666666666666*(f.y-this.currentY_)};Q(this,g,{x:g.x+(h.x-this.currentX_)/3,y:g.y+(h.y-this.currentY_)/3},h)};i.arc=function(b,a,c,d,f,h){c*=k;var g=h?"at":"wa",l=b+G(d)*c-v,e=a+F(d)*c-v,m=b+G(f)*c-v,r=a+F(f)*c-v;if(l==m&&!h)l+=0.125;var n=this.getCoords_(b,a),o=this.getCoords_(l,e),q=this.getCoords_(m,r);this.currentPath_.push({type:g,x:n.x,y:n.y,radius:c,xStart:o.x,yStart:o.y,xEnd:q.x,yEnd:q.y})};i.rect=function(b,a,c,d){this.moveTo(b,
23
+ a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath()};i.strokeRect=function(b,a,c,d){var f=this.currentPath_;this.beginPath();this.moveTo(b,a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath();this.stroke();this.currentPath_=f};i.fillRect=function(b,a,c,d){var f=this.currentPath_;this.beginPath();this.moveTo(b,a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath();this.fill();this.currentPath_=f};i.createLinearGradient=function(b,
24
+ a,c,d){var f=new D("gradient");f.x0_=b;f.y0_=a;f.x1_=c;f.y1_=d;return f};i.createRadialGradient=function(b,a,c,d,f,h){var g=new D("gradientradial");g.x0_=b;g.y0_=a;g.r0_=c;g.x1_=d;g.y1_=f;g.r1_=h;return g};i.drawImage=function(b){var a,c,d,f,h,g,l,e,m=b.runtimeStyle.width,r=b.runtimeStyle.height;b.runtimeStyle.width="auto";b.runtimeStyle.height="auto";var n=b.width,o=b.height;b.runtimeStyle.width=m;b.runtimeStyle.height=r;if(arguments.length==3){a=arguments[1];c=arguments[2];h=g=0;l=d=n;e=f=o}else if(arguments.length==
25
+ 5){a=arguments[1];c=arguments[2];d=arguments[3];f=arguments[4];h=g=0;l=n;e=o}else if(arguments.length==9){h=arguments[1];g=arguments[2];l=arguments[3];e=arguments[4];a=arguments[5];c=arguments[6];d=arguments[7];f=arguments[8]}else throw Error("Invalid number of arguments");var q=this.getCoords_(a,c),t=[];t.push(" <g_vml_:group",' coordsize="',k*10,",",k*10,'"',' coordorigin="0,0"',' style="width:',10,"px;height:",10,"px;position:absolute;");if(this.m_[0][0]!=1||this.m_[0][1]){var E=[];E.push("M11=",
26
+ this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",j(q.x/k),",","Dy=",j(q.y/k),"");var p=q,z=this.getCoords_(a+d,c),w=this.getCoords_(a,c+f),x=this.getCoords_(a+d,c+f);p.x=s.max(p.x,z.x,w.x,x.x);p.y=s.max(p.y,z.y,w.y,x.y);t.push("padding:0 ",j(p.x/k),"px ",j(p.y/k),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",E.join(""),", sizingmethod='clip');")}else t.push("top:",j(q.y/k),"px;left:",j(q.x/k),"px;");t.push(' ">','<g_vml_:image src="',b.src,
27
+ '"',' style="width:',k*d,"px;"," height:",k*f,'px;"',' cropleft="',h/n,'"',' croptop="',g/o,'"',' cropright="',(n-h-l)/n,'"',' cropbottom="',(o-g-e)/o,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",t.join(""))};i.stroke=function(b){var a=[],c=P(b?this.fillStyle:this.strokeStyle),d=c.color,f=c.alpha*this.globalAlpha;a.push("<g_vml_:shape",' filled="',!!b,'"',' style="position:absolute;width:',10,"px;height:",10,'px;"',' coordorigin="0 0" coordsize="',k*10," ",k*10,'"',' stroked="',
28
+ !b,'"',' path="');var h={x:null,y:null},g={x:null,y:null},l=0;for(;l<this.currentPath_.length;l++){var e=this.currentPath_[l];switch(e.type){case "moveTo":a.push(" m ",j(e.x),",",j(e.y));break;case "lineTo":a.push(" l ",j(e.x),",",j(e.y));break;case "close":a.push(" x ");e=null;break;case "bezierCurveTo":a.push(" c ",j(e.cp1x),",",j(e.cp1y),",",j(e.cp2x),",",j(e.cp2y),",",j(e.x),",",j(e.y));break;case "at":case "wa":a.push(" ",e.type," ",j(e.x-this.arcScaleX_*e.radius),",",j(e.y-this.arcScaleY_*e.radius),
29
+ " ",j(e.x+this.arcScaleX_*e.radius),",",j(e.y+this.arcScaleY_*e.radius)," ",j(e.xStart),",",j(e.yStart)," ",j(e.xEnd),",",j(e.yEnd));break}if(e){if(h.x==null||e.x<h.x)h.x=e.x;if(g.x==null||e.x>g.x)g.x=e.x;if(h.y==null||e.y<h.y)h.y=e.y;if(g.y==null||e.y>g.y)g.y=e.y}}a.push(' ">');if(b)if(typeof this.fillStyle=="object"){var m=this.fillStyle,r=0,n={x:0,y:0},o=0,q=1;if(m.type_=="gradient"){var t=m.x1_/this.arcScaleX_,E=m.y1_/this.arcScaleY_,p=this.getCoords_(m.x0_/this.arcScaleX_,m.y0_/this.arcScaleY_),
30
+ z=this.getCoords_(t,E);r=Math.atan2(z.x-p.x,z.y-p.y)*180/Math.PI;if(r<0)r+=360;if(r<1.0E-6)r=0}else{var p=this.getCoords_(m.x0_,m.y0_),w=g.x-h.x,x=g.y-h.y;n={x:(p.x-h.x)/w,y:(p.y-h.y)/x};w/=this.arcScaleX_*k;x/=this.arcScaleY_*k;var R=s.max(w,x);o=2*m.r0_/R;q=2*m.r1_/R-o}var u=m.colors_;u.sort(function(ba,ca){return ba.offset-ca.offset});var J=u.length,da=u[0].color,ea=u[J-1].color,fa=u[0].alpha*this.globalAlpha,ga=u[J-1].alpha*this.globalAlpha,S=[],l=0;for(;l<J;l++){var T=u[l];S.push(T.offset*q+
31
+ o+" "+T.color)}a.push('<g_vml_:fill type="',m.type_,'"',' method="none" focus="100%"',' color="',da,'"',' color2="',ea,'"',' colors="',S.join(","),'"',' opacity="',ga,'"',' g_o_:opacity2="',fa,'"',' angle="',r,'"',' focusposition="',n.x,",",n.y,'" />')}else a.push('<g_vml_:fill color="',d,'" opacity="',f,'" />');else{var K=this.lineScale_*this.lineWidth;if(K<1)f*=K;a.push("<g_vml_:stroke",' opacity="',f,'"',' joinstyle="',this.lineJoin,'"',' miterlimit="',this.miterLimit,'"',' endcap="',aa(this.lineCap),
32
+ '"',' weight="',K,'px"',' color="',d,'" />')}a.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",a.join(""))};i.fill=function(){this.stroke(true)};i.closePath=function(){this.currentPath_.push({type:"close"})};i.getCoords_=function(b,a){var c=this.m_;return{x:k*(b*c[0][0]+a*c[1][0]+c[2][0])-v,y:k*(b*c[0][1]+a*c[1][1]+c[2][1])-v}};i.save=function(){var b={};O(this,b);this.aStack_.push(b);this.mStack_.push(this.m_);this.m_=y(I(),this.m_)};i.restore=function(){O(this.aStack_.pop(),
33
+ this);this.m_=this.mStack_.pop()};function ha(b){var a=0;for(;a<3;a++){var c=0;for(;c<2;c++)if(!isFinite(b[a][c])||isNaN(b[a][c]))return false}return true}function A(b,a,c){if(!!ha(a)){b.m_=a;if(c)b.lineScale_=W(V(a[0][0]*a[1][1]-a[0][1]*a[1][0]))}}i.translate=function(b,a){A(this,y([[1,0,0],[0,1,0],[b,a,1]],this.m_),false)};i.rotate=function(b){var a=G(b),c=F(b);A(this,y([[a,c,0],[-c,a,0],[0,0,1]],this.m_),false)};i.scale=function(b,a){this.arcScaleX_*=b;this.arcScaleY_*=a;A(this,y([[b,0,0],[0,a,
34
+ 0],[0,0,1]],this.m_),true)};i.transform=function(b,a,c,d,f,h){A(this,y([[b,a,0],[c,d,0],[f,h,1]],this.m_),true)};i.setTransform=function(b,a,c,d,f,h){A(this,[[b,a,0],[c,d,0],[f,h,1]],true)};i.clip=function(){};i.arcTo=function(){};i.createPattern=function(){return new U};function D(b){this.type_=b;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}D.prototype.addColorStop=function(b,a){a=P(a);this.colors_.push({offset:b,color:a.color,alpha:a.alpha})};function U(){}G_vmlCanvasManager=
35
+ M;CanvasRenderingContext2D=H;CanvasGradient=D;CanvasPattern=U})();
js/mageho/sortproducts/loading.gif ADDED
Binary file
js/mageho/sortproducts/opentip.css ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ ** http://www.opentip.org
3
+ ** See opentip.js for the license.
4
+ **
5
+ ** One word about the different states when showing / hiding opentips.
6
+ **
7
+ ** An opentip starts with the .ot-completely-hidden class.
8
+ ** When shown, it changes to .ot-becoming-visible, then to .ot-visible (depending
9
+ ** on the length of the transition effect).
10
+ ** When hidden, it changes to .ot-hidden, and then to .ot-completely-hidden (again:
11
+ ** depending on the length of the transition effect).
12
+ **/
13
+ .ot-container {
14
+ position: absolute;
15
+ max-width: 300px;
16
+ z-index: 100;
17
+ }
18
+ /**
19
+ * Only using the position transition on fixed opentips, so the tip only moves
20
+ * smoothly when it changes position due to browser viewport changes.
21
+ */
22
+ .ot-container.ot-fixed {
23
+ -webkit-transition-property: left, top;
24
+ -webkit-transition-duration: 0.2s, 0.2s;
25
+ -moz-transition-property: left, top;
26
+ -moz-transition-duration: 0.2s, 0.2s;
27
+ }
28
+ .ot-container.ot-completely-hidden {
29
+ display: none;
30
+ }
31
+ .opentip {
32
+ position: relative;
33
+ font-size: 13px;
34
+ line-height: 120%;
35
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
36
+ -moz-box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
37
+ -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
38
+ }
39
+ .opentip .title { font-weight: bold; }
40
+ .opentip .content { width: auto !important;}
41
+
42
+
43
+ .opentip .loadingIndication {
44
+ display: none;
45
+ padding: 10px 15px;
46
+ background: url("loading.gif") center center no-repeat;
47
+ width: 25px;
48
+ height: 15px;
49
+ }
50
+ .opentip .loadingIndication span { display: none; }
51
+ .ot-loading .opentip .loadingIndication { display: block; }
52
+ .ot-loading .opentip .content { display: none; }
53
+
54
+ .ot-buttons {
55
+ position: absolute;
56
+ right: 0;
57
+ top: 0;
58
+ }
59
+
60
+ .ot-buttons .close {
61
+ display: block;
62
+ width: 15px;
63
+ height: 15px;
64
+ display: block;
65
+ text-decoration: none;
66
+ }
67
+ .ot-buttons .close .canvas {
68
+ position: relative;
69
+ display: block;
70
+ color: white; /* Read by JS and applied to canvas */
71
+ background-color: rgba(0, 0, 0, 0.2); /* Read by JS and applied to canvas */
72
+ }
73
+
74
+ .ot-container .stem-container {
75
+ position: absolute;
76
+ width: 0;
77
+ height: 0;
78
+ }
79
+ .ot-container .stem {
80
+ position: absolute;
81
+ overflow: hidden;
82
+ color: #cccccc; /* JS will read this property to draw the stem in the right color. */
83
+ /* background: rgba(255, 0, 0, 0.5);*/
84
+ }
85
+
86
+
87
+ .ot-container .left { left: 0; }
88
+ .ot-container .center { left: 50%; }
89
+ .ot-container .right { right: 0; }
90
+ .ot-container .top { top: 0; }
91
+ .ot-container .middle { top: 50%; }
92
+ .ot-container .bottom { bottom: 0; }
93
+
94
+
95
+
96
+ /**
97
+
98
+ Styles
99
+ ======
100
+
101
+ Those are the different styles available in opentip.
102
+
103
+ **/
104
+
105
+ .style-standard .opentip {
106
+ border: 1px solid #f2e37b;
107
+ background-color: #fff18f;
108
+ color: black;
109
+ padding: 6px 10px;
110
+ box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.1);
111
+ -moz-box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.1);
112
+ -webkit-box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.1);
113
+ }
114
+ .style-standard .opentip .title {
115
+ margin-bottom: 1px;
116
+ }
117
+ .style-standard .stem {
118
+ color: #e3ca1b; /* JS will read this property to draw the stem in the right color. */
119
+ }
120
+ .style-standard .ot-buttons {
121
+ top: -6px;
122
+ right: -6px;
123
+ }
124
+ .style-standard .ot-buttons .close {
125
+ width: 21px;
126
+ height: 21px;
127
+ }
128
+ .style-standard .ot-buttons .close .canvas {
129
+ background-color: rgba(255, 241, 143, 0.5);
130
+ color: #a2932B;
131
+ }
132
+
133
+
134
+
135
+
136
+ .style-rounded .opentip {
137
+ border: 7px solid #81b4da;
138
+ background-color: #f9fbfc;
139
+ color: #3f5d73;
140
+ border-radius: 10px;
141
+ -moz-border-radius: 10px;
142
+ -webkit-border-radius: 10px;
143
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
144
+ -moz-box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
145
+ -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
146
+ }
147
+ .style-rounded .opentip .title {
148
+ background: #81b4da;
149
+ color: #f7fafd;
150
+ padding: 0px 10px 4px 10px;
151
+ }
152
+ .style-rounded .opentip .content {
153
+ padding: 6px 10px;
154
+ }
155
+ .style-rounded .stem {
156
+ color: #c3ddf0;
157
+ }
158
+ .style-rounded .ot-buttons {
159
+ top: -3px;
160
+ right: -3px;
161
+ }
162
+ .style-rounded .ot-buttons .close {
163
+ width: 21px;
164
+ height: 21px;
165
+ }
166
+ .style-rounded .ot-buttons .close .canvas {
167
+ background-color: #81B4DA;
168
+ }
169
+
170
+ .style-slick .opentip {
171
+ border: 1px solid #eeeeee;
172
+ background: #f7f7f7;
173
+ border-radius: 3px;
174
+ -moz-border-radius: 3px;
175
+ -webkit-border-radius: 3px;
176
+ }
177
+ .style-slick .opentip .title {
178
+ /* color: #49677e !important; */
179
+ color:#fff !important;
180
+ padding: 5px 10px 3px 10px !important;
181
+ border-bottom: 1px solid #eeeeee !important;
182
+ }
183
+ .style-slick .opentip .content {
184
+ padding: 6px 10px;
185
+ border-top: 1px solid #fefefe;
186
+ }
187
+ .style-slick .stem {
188
+ color: #cccccc;
189
+ }
190
+ .style-slick .ot-buttons {
191
+ right: -7px;
192
+ top: -5px;
193
+ }
194
+ .style-slick .ot-buttons .close {
195
+ height: 19px;
196
+ width: 19px;
197
+ }
198
+ .style-slick .ot-buttons .close .canvas {
199
+ background-color: rgba(0, 0, 0, 0.2);
200
+ }
201
+
202
+
203
+ .style-glass .opentip {
204
+ background: white;
205
+ background: rgba(255, 255, 255, 0.9);
206
+ border-radius: 5px;
207
+ -moz-border-radius: 5px;
208
+ -webkit-border-radius: 5px;
209
+ box-shadow: 0 0 15px rgba(51, 113, 136, 0.5);
210
+ -moz-box-shadow: 0 0 15px rgba(51, 113, 136, 0.5);
211
+ -webkit-box-shadow: 0 0 15px rgba(51, 113, 136, 0.5);
212
+ padding: 20px 30px;
213
+ }
214
+ .style-glass .opentip .title {
215
+ color: #316F89;
216
+ font-family: serif;
217
+ font-style: italic;
218
+ margin: 0 0 20px 0;
219
+ font-size: 16px;
220
+ }
221
+ .style-glass .opentip .content {
222
+ color: #333333;
223
+ }
224
+ .style-glass .stem {
225
+ color: #ffffff;
226
+ }
227
+ .style-glass .ot-buttons {
228
+ right: -3px;
229
+ top: -3px;
230
+ }
231
+ .style-glass .ot-buttons .close {
232
+ width: 25px;
233
+ height: 25px;
234
+ }
235
+ .style-glass .ot-buttons .close .canvas {
236
+ background-color: #F7BA00;
237
+ }
238
+
239
+ /**
240
+
241
+ IE 6 HACKS
242
+ ==========
243
+
244
+ **/
245
+
246
+ /*\*/
247
+ .ot-buttons {
248
+ z-index: 110;
249
+ }
250
+ .opentip {
251
+ z-index: 110;
252
+ }
253
+ .opentipIFrame {
254
+ position: absolute;
255
+ top: 0;
256
+ left: 0;
257
+ border: none;
258
+ background: none;
259
+ margin: 0;
260
+ padding: 0;
261
+ display:none;
262
+ /*sorry for IE5*/ display/**/:block;/*sorry for IE5*/
263
+ z-index:-1;/*must have*/
264
+ filter:mask();/*must have*/
265
+ }
266
+
267
+ .style-glass .opentip {
268
+ border: 1px solid #bbbbbb;
269
+ }
270
+ /**/
271
+
272
+
273
+
274
+
275
+ /**
276
+
277
+ CSS3 Transitions
278
+ ================
279
+
280
+ The definitions that follow here, are only meant for browsers that support css3
281
+ transitions.
282
+
283
+ So the syntax I'm going to use, is really meant for more modern browsers than
284
+ IE6.
285
+
286
+
287
+
288
+ To define a show effect, you have to define the position you want to come from
289
+ as the .ot-completely-hidden property (in combination with your
290
+ .ot-show-effectName).
291
+
292
+ To define a hide effect, you do the same, but for .ot-hidden (in combination
293
+ with your .ot-show-effectName).
294
+
295
+ **/
296
+
297
+
298
+ .ot-container.ot-css3 {
299
+ -webkit-transition-duration: 1s; /* Well be reset by JS */
300
+ -webkit-transition-property: opacity, -webkit-transform;
301
+ -moz-transition-duration: 1s; /* Well be reset by JS */
302
+ -moz-transition-property: opacity, -moz-transform;
303
+ -o-transition-duration: 1s; /* Well be reset by JS */
304
+ -o-transition-property: opacity, -o-transform;
305
+
306
+ opacity: 1;
307
+ }
308
+
309
+ .ot-container.ot-css3.ot-completely-hidden {
310
+ display: none;
311
+ }
312
+
313
+
314
+
315
+
316
+
317
+ /** Appear / Fade**/
318
+ .ot-container.ot-css3.ot-becoming-visible.ot-show-appear,
319
+ .ot-container.ot-css3.ot-hidden.ot-hide-fade {
320
+ opacity: 0;
321
+ }
322
+
323
+
324
+
325
+ /** Grow / Shrink **/
326
+ .ot-container.ot-css3.ot-becoming-visible.ot-show-grow,
327
+ .ot-container.ot-css3.ot-hidden.ot-hide-shrink {
328
+ -webkit-transform: scale(0);
329
+ -moz-transform: scale(0);
330
+ -o-transform: scale(0);
331
+ }
332
+
333
+
334
+
335
+ /** Blind/Slide down / Blind up**/
336
+ .ot-container.ot-css3.ot-becoming-visible.ot-show-blindDown,
337
+ .ot-container.ot-css3.ot-becoming-visible.ot-show-slideDown,
338
+ .ot-container.ot-css3.ot-hidden.ot-hide-blindUp {
339
+ -webkit-transform: scaleY(0);
340
+ -moz-transform: scaleY(0);
341
+ -o-transform: scaleY(0);
342
+ opacity: 0;
343
+ }
344
+
345
+
346
+ /** Condense / Puff **/
347
+ .ot-container.ot-css3.ot-becoming-visible.ot-show-condense,
348
+ .ot-container.ot-css3.ot-hidden.ot-hide-puff {
349
+ -webkit-transform: scale(5);
350
+ -moz-transform: scale(5);
351
+ -o-transform: scale(5);
352
+ opacity: 0;
353
+ }
354
+
355
+ /** Rotate **/
356
+ .ot-container.ot-css3.ot-becoming-visible.ot-show-rotate {
357
+ -webkit-transform: scale(3) rotate(-500deg);
358
+ -moz-transform: scale(3) rotate(-500deg);
359
+ -o-transform: scale(3) rotate(-500deg);
360
+ opacity: 0;
361
+ }
362
+ .ot-container.ot-css3.ot-hidden.ot-hide-rotate {
363
+ -webkit-transform: scale(3) rotate(500deg);
364
+ -moz-transform: scale(3) rotate(500deg);
365
+ -o-transform: scale(3) rotate(500deg);
366
+ opacity: 0;
367
+ }
368
+
369
+
370
+
371
+
372
+
373
+
374
+
375
+
376
+
377
+
378
+
379
+
380
+ /**
381
+
382
+ Content design
383
+ ==============
384
+
385
+ Nicer input fields, etc...
386
+
387
+ */
388
+
389
+
390
+ .opentip label {
391
+ margin-bottom: 3px;
392
+ margin-top: 10px;
393
+ display: block;
394
+ }
395
+ .opentip input, .opentip textarea {
396
+ padding: 5px 6px;
397
+ border: 1px solid rgba(100, 100, 100, 0.2);
398
+ background: rgba(255,255,255,0.5);
399
+ display: block;
400
+ width: 100%;
401
+ margin: 3px 0 10px 0;
402
+ -moz-border-radius: 2px;
403
+ -webkit-border-radius: 2px;
404
+ -moz-box-sizing: border-box;
405
+ -webkit-box-sizing: border-box;
406
+ }
407
+ .opentip input:focus, .opentip textarea:focus {
408
+ border-color: rgba(100, 100, 100, 0.2);
409
+ -moz-box-shadow: 0 0 10px rgba(0,0,0,0.2);
410
+ -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.2);
411
+ }
412
+
413
+ .opentip button {
414
+ margin-top: 20px;
415
+ display: block;
416
+ -moz-box-sizing: border-box;
417
+ -webkit-box-sizing: border-box;
418
+ box-sizing: border-box;
419
+ width: 100%;
420
+
421
+
422
+ border: 1px solid rgba(247, 186, 0, 0.8);
423
+ background: rgba(247, 186, 0, 0.9);
424
+ font-size: 14px;
425
+ line-height: 14px;
426
+ padding: 10px 10px;
427
+ position: relative;
428
+ color: rgba(255,255,255, 1);
429
+ text-shadow: 0 0 10px rgba(255,255,255,0.3);
430
+ text-align: center;
431
+ font-weight: bold;
432
+ font-family: serif;
433
+ font-style: italic;
434
+ text-decoration: none;
435
+ margin: 20px 0 0 0;
436
+ cursor: pointer;
437
+ -moz-border-radius: 2px;
438
+ -webkit-border-radius: 2px;
439
+ -moz-box-shadow: 0 0 4px rgba(0,0,0,0.2),
440
+ 0 -2px 10px rgba(255, 255, 255, 0.4) inset;
441
+ -webkit-box-shadow: 0 0 4px rgba(0,0,0,0.2),
442
+ 0 -2px 10px rgba(255, 255, 255, 0.4) inset;
443
+ }
444
+ .opentip button {
445
+ padding: 8px 6px;
446
+ }
447
+ .button:hover, button:hover {
448
+ -moz-box-shadow: 0 0 6px rgba(247, 192, 25, 1),
449
+ 0 -2px 10px rgba(255, 255, 255, 0.6) inset;
450
+ text-shadow: 0 0 4px rgba(255,255,255,1);
451
+ }
452
+
453
+
454
+
455
+ /** IE 6 HACKS **/
456
+ /*\*/
457
+ .opentip input, .opentip textarea, .opentip button {
458
+ width: 200px;
459
+ }
460
+ /**/
js/mageho/sortproducts/opentip.js ADDED
@@ -0,0 +1,1285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ ** More info at http://www.opentip.org
3
+ **
4
+ ** Copyright (c) 2009, Matias Meno
5
+ ** Graphics by Tjandra Mayerhold
6
+ **
7
+ ** Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ ** of this software and associated documentation files (the "Software"), to deal
9
+ ** in the Software without restriction, including without limitation the rights
10
+ ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ ** copies of the Software, and to permit persons to whom the Software is
12
+ ** furnished to do so, subject to the following conditions:
13
+ **
14
+ ** The above copyright notice and this permission notice shall be included in
15
+ ** all copies or substantial portions of the Software.
16
+ **
17
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ ** THE SOFTWARE.
24
+ **
25
+ **/
26
+
27
+
28
+ /**
29
+ ** Usage:
30
+ **
31
+ ** <div onmouseover="javascript:Tips.add(this, event, 'Content', { options });"></div>
32
+ **
33
+ ** or externally:
34
+ **
35
+ ** $('elementId').addTip('Content', { options });
36
+ **
37
+ ** For a full documentation, please visit http://www.opentip.org/documentation
38
+ **/
39
+
40
+
41
+
42
+ /**
43
+ * Namespace and helper functions for opentips.
44
+ */
45
+ var Opentip = {
46
+
47
+ Version: '1.4.4',
48
+ REQUIRED_PROTOTYPE_VERSION: '1.6.0',
49
+ REQUIRED_SCRIPTACULOUS_VERSION: '1.8.0',
50
+ STICKS_OUT_TOP: 1,
51
+ STICKS_OUT_BOTTOM: 2,
52
+ STICKS_OUT_LEFT: 1,
53
+ STICKS_OUT_RIGHT: 2,
54
+ cached: {},
55
+ debugging: false,
56
+ load: function() {
57
+ function getComparableVersion(version) {var v = version.split('.');return parseInt(v[0])*100000 + parseInt(v[1])*1000 + parseInt(v[2]);}
58
+ if((typeof Prototype === 'undefined') || (typeof Element === 'undefined') || (typeof Element.Methods === 'undefined') || (getComparableVersion(Prototype.Version) < getComparableVersion(Opentip.REQUIRED_PROTOTYPE_VERSION))) {throw("Opentip requires the Prototype JavaScript framework >= " + Opentip.REQUIRED_PROTOTYPE_VERSION);}
59
+
60
+ Opentip.useCss3Transitions = Opentip.supports('transition');
61
+ Opentip.useScriptaculousTransitions = ! Opentip.useCss3Transitions;
62
+
63
+ if (Opentip.useCss3Transitions) Opentip.debug('Using CSS3 transitions.');
64
+
65
+ if((typeof Scriptaculous === 'undefined') || (typeof Effect === 'undefined') || (getComparableVersion(Scriptaculous.Version) < getComparableVersion(Opentip.REQUIRED_SCRIPTACULOUS_VERSION))) {
66
+ Opentip.debug('No scriptaculous available. Disabling scriptaculous transitions.');
67
+ Opentip.useScriptaculousTransitions = false;
68
+ }
69
+ },
70
+ debug: function() {if (this.debugging && typeof console !== 'undefined' && typeof console.debug !== 'undefined') console.debug.apply(console, arguments);},
71
+ IEVersion: function() {
72
+ if (typeof Opentip.cached.IEVersion !== 'undefined') return Opentip.cached.IEVersion;
73
+ if (Prototype.Browser.IE) {
74
+ var version = navigator.userAgent.match('MSIE ([\\d.]+)');
75
+ var IEVersion = version ? (parseFloat(version[1])) : false;
76
+ }
77
+ else IEVersion = false;
78
+ Opentip.cached.IEVersion = IEVersion;
79
+ return IEVersion;
80
+ },
81
+ objectIsEvent: function(obj) {
82
+ // There must be a better way of doing this.
83
+ return (typeof(obj) == 'object' && obj.type && obj.screenX);
84
+ },
85
+ useIFrame: function() {return Opentip.IEVersion() ? (Opentip.IEVersion() <= 6) : false;},
86
+ lastTipId: 1,
87
+ lastZIndex: 100,
88
+ documentIsLoaded: false,
89
+ postponeCreation: function(createFunction) {
90
+ if (Opentip.documentIsLoaded || !Opentip.IEVersion()) createFunction();
91
+ else {
92
+ Event.observe(window, 'load', createFunction); // Sorry IE users but... well: get another browser!
93
+ }
94
+ },
95
+
96
+ // Mimics scriptaculous Builder.node behaviour
97
+ element: function(tagName, attributes, children) {
98
+ if (Object.isArray(attributes) || Object.isString(attributes) || Object.isElement(attributes)) {
99
+ children = attributes;
100
+ attributes = null;
101
+ }
102
+
103
+ var element = new Element(tagName, attributes || {});
104
+
105
+ // This is a prototype 1.6 bug, that doesn't apply the className to IE8 elements.
106
+ // Thanks to Alexander Shakhnovsky for finding the bug, and pinpointing the problem.
107
+ if(attributes && attributes['className']) {
108
+ attributes['className'].split(' ').each(function(class_name){element.addClassName(class_name);});
109
+ }
110
+
111
+ if (children) {
112
+ if (Object.isArray(children)) {
113
+ children.each(function(child) {
114
+ element.insert({bottom: child});
115
+ });
116
+ }
117
+ else {
118
+ element.insert({bottom: children});
119
+ }
120
+ }
121
+ return element;
122
+ },
123
+
124
+ // In the future every position attribute will go through this method.
125
+ sanitizePosition: function(arrayPosition) {
126
+ var position;
127
+ if (Object.isArray(arrayPosition)) {
128
+ var positionString = '';
129
+ if (arrayPosition[0] == 'center') {
130
+ positionString = arrayPosition[1];
131
+ }
132
+ else if (arrayPosition[1] == 'middle') {
133
+ positionString = arrayPosition[0];
134
+ }
135
+ else {
136
+ positionString = arrayPosition[1] + arrayPosition[0].capitalize();
137
+ }
138
+ if (Opentip.position[positionString] === undefined) throw 'Unknown position: ' + positionString;
139
+ position = Opentip.position[positionString];
140
+ }
141
+ else if (Object.isString(arrayPosition)) {
142
+ if (Opentip.position[arrayPosition] === undefined) throw 'Unknown position: ' + arrayPosition;
143
+ position = Opentip.position[arrayPosition];
144
+ }
145
+ return parseInt(position);
146
+ },
147
+
148
+
149
+ /* Browser support testing */
150
+ vendors: 'Khtml Ms O Moz Webkit'.split(' '),
151
+ testDiv: document.createElement('div'),
152
+ supports: function(prop) {
153
+ if ( prop in Opentip.testDiv.style ) return true;
154
+
155
+ prop = prop.ot_ucfirst();
156
+
157
+ return Opentip.vendors.any(function(vendor) {
158
+ return vendor + prop in Opentip.testDiv.style;
159
+ });
160
+ }
161
+ };
162
+
163
+ String.prototype.ot_ucfirst = function() {
164
+ return this.replace(/^\w/, function(val) {return val.toUpperCase();});
165
+ };
166
+
167
+ Opentip.load();
168
+
169
+
170
+
171
+
172
+
173
+ /**
174
+ * The standard style.
175
+ */
176
+
177
+ Opentip.styles = {
178
+ standard: {
179
+ // This style contains all default values for other styles.
180
+ // POSITION : [ 'left|right|center', 'top|bottom|middle' ]
181
+ // COORDINATE : [ XVALUE, YVALUE ] (integers)
182
+ title: null,
183
+ className: 'standard', // The class name to be used in the stylesheet
184
+ stem: false, // false (no stem) || true (stem at tipJoint position) || POSITION (for stems in other directions)
185
+ delay: null, // float (in seconds - if null, the default is used: 0.2 for mouseover, 0 for click)
186
+ hideDelay: 0.1, // --
187
+ fixed: false, // If target is not null, elements are always fixed.
188
+ showOn: 'mouseover', // string (the observe string of the trigger element, eg: click, mouseover, etc..) || 'creation' (the tooltip will show when being created) || null if you want to handle it yourself.
189
+ hideTrigger: 'trigger', // 'trigger' | 'tip' | 'target' | 'closeButton' | ELEMENT | ELEMENT_ID || array containing any of the previous
190
+ hideOn: null, // string (event eg: click) || array of event strings if multiple hideTriggers || null (let Opentip decide)
191
+ offset: [ 0, 0 ], // COORDINATE
192
+ containInViewport: true, // Whether the targetJoint/tipJoint should be changed if the tooltip is not in the viewport anymore.
193
+ autoOffset: true, // If set to true, offsets are calculated automatically to position the tooltip. (pixels are added if there are stems for example)
194
+ showEffect: 'appear', // scriptaculous or CSS3 (in opentip.css) effect
195
+ fallbackShowEffect: 'appear', // At tip creation, this effect will override the showEffect, if useScriptaculousTransitions == true, and the showEffect does not exist.
196
+ hideEffect: 'fade',
197
+ fallbackHideEffect: 'appear',
198
+ showEffectDuration: 0.3,
199
+ hideEffectDuration: 0.2,
200
+ stemSize: 8, // integer
201
+ tipJoint: [ 'left', 'top' ], // POSITION
202
+ target: null, // null (no target, opentip uses mouse as target) || true (target is the triggerElement) || elementId|element (for another element)
203
+ targetJoint: null, // POSITION (Ignored if target == null) || null (targetJoint is the opposite of tipJoint)
204
+ ajax: false, // Ajax options. eg: { url: 'yourUrl.html', options: { ajaxOptions... } } or { options: { ajaxOptions } /* This will use the href of the A element the tooltip is attached to */ }
205
+ group: null, // You can group opentips together. So when a tooltip shows, it looks if there are others in the same group, and hides them.
206
+ escapeHtml: false,
207
+ style: null
208
+ },
209
+ slick: {
210
+ className: 'slick',
211
+ stem: true
212
+ },
213
+ rounded: {
214
+ className: 'rounded',
215
+ stem: true
216
+ },
217
+ glass: {
218
+ className: 'glass'
219
+ }
220
+ };
221
+ Opentip.defaultStyle = 'standard'; // Change this to the style name you want your tooltips to have.
222
+
223
+
224
+
225
+ Opentip.position = {
226
+ top: 0,
227
+ topRight: 1,
228
+ right: 2,
229
+ bottomRight: 3,
230
+ bottom: 4,
231
+ bottomLeft: 5,
232
+ left: 6,
233
+ topLeft: 7
234
+ };
235
+
236
+
237
+
238
+ /**
239
+ * On document load
240
+ */
241
+ Event.observe(window, Opentip.IEVersion() ? 'load' : 'dom:loaded', function() {
242
+ Opentip.documentIsLoaded = true;
243
+
244
+
245
+ var htmlOptionNames = [];
246
+ for (var i in Opentip.styles.standard) {
247
+ htmlOptionNames.push(i.underscore().dasherize());
248
+ }
249
+
250
+ // Go through all elements, and look for elements that have inline element
251
+ // opentip definitions.
252
+ $$('[data-ot]').each(function(element) {
253
+ var options = {};
254
+ element = $(element);
255
+
256
+ var content = element.readAttribute('data-ot');
257
+
258
+ if (content === '' || content === 'true' || content === 'yes') {
259
+ content = element.readAttribute('title');
260
+ element.title = '';
261
+ }
262
+
263
+
264
+ content || (content = '');
265
+
266
+ htmlOptionNames.each(function(optionName) {
267
+ var optionValue;
268
+ if (optionValue = element.readAttribute('data-ot-' + optionName)) {
269
+ try {
270
+ // See if it's a JSON string.
271
+ optionValue = optionValue.gsub("'", '"').evalJSON();
272
+ }
273
+ catch (err) {
274
+ // Well, it's not.
275
+ }
276
+
277
+ options[optionName.camelize()] = optionValue;
278
+ }
279
+ });
280
+
281
+ element.addTip(content, options);
282
+ });
283
+ });
284
+
285
+
286
+
287
+
288
+
289
+
290
+ var Tips = {
291
+ list: [],
292
+ append: function(tip) {this.list.push(tip);},
293
+ remove: function(element) {
294
+ if (!element.element) var tip = this.list.find(function(t) {return t.triggerElement === element});
295
+ else var tip = this.list.find(function(t) {return t === element});
296
+ if (tip) {
297
+ tip.deactivate();
298
+ tip.destroyAllElements();
299
+ this.list = this.list.without(tip);
300
+ }
301
+ },
302
+ add: function(element) {
303
+ if (element._opentipAddedTips) {
304
+ /* TODO: Now it just returns the first found... try to find the correct one. */
305
+ var tip = this.list.find(function(t) {return (t.triggerElement === element);});
306
+ if (tip.options.showOn == 'creation') tip.show();
307
+ tip.debug('Using an existing opentip.');
308
+ return;
309
+ } else setTimeout(function() {element._opentipAddedTips = true;}, 1); // I added a timeout, so that tooltips, defined in an onmouseover or onclick event, will show.
310
+
311
+ Opentip.debug('Creating new opentip');
312
+
313
+ var tipArguments = [];
314
+
315
+ $A(arguments).each(function(arg, idx) {
316
+ if (idx == 1 && !Opentip.objectIsEvent(arg)) tipArguments.push(null);
317
+ tipArguments.push(arg);
318
+ });
319
+
320
+
321
+ // Creating the tooltip object, but not yet activating it, or creating the container elements.
322
+ var tooltip = new TipClass(tipArguments[0], tipArguments[1], tipArguments[2], tipArguments[3], tipArguments[4]);
323
+
324
+ this.append(tooltip);
325
+
326
+ var self = this;
327
+ var createTip = function() {
328
+ tooltip.create(tipArguments[1]); // Passing the event.
329
+ }
330
+
331
+ Opentip.postponeCreation(createTip);
332
+
333
+ return tooltip;
334
+ },
335
+ hideGroup: function(groupName) {
336
+ this.list.findAll(function(t) {return (t.options.group == groupName);}).invoke('doHide');
337
+ },
338
+ abortShowingGroup: function(groupName) {
339
+ this.list.findAll(function(t) {return (t.options.group == groupName);}).invoke('abortShowing');
340
+ }
341
+ };
342
+
343
+ var Tip = function() { return Tips.add.apply(Tips, arguments); };
344
+
345
+ Element.addMethods({
346
+ addTip: function(element) {
347
+ element = $(element);
348
+ Tips.add.apply(Tips, arguments);
349
+ return element;
350
+ },
351
+ setCss3Style: function(element) {
352
+ element = $(element);
353
+ var style = {};
354
+ for (var propertyName in arguments[1]) {
355
+ var css3PropertyName = propertyName.ot_ucfirst();
356
+ var css3PropertyValue = arguments[1][propertyName];
357
+ Opentip.vendors.each(function(vendor) {
358
+ style[vendor + css3PropertyName] = css3PropertyValue;
359
+ element.setStyle(style);
360
+ });
361
+ }
362
+ return element;
363
+ }
364
+ });
365
+
366
+
367
+ var TipClass = Class.create({
368
+ debug: function() {
369
+ var newArguments = Array.from(arguments);
370
+ newArguments.unshift('ID:', this.id, '|');
371
+ Opentip.debug.apply(Opentip, newArguments);
372
+ },
373
+ initialize: function(element, evt) {
374
+ this.id = Opentip.lastTipId ++;
375
+
376
+ element = $(element);
377
+
378
+ this.triggerElement = element;
379
+
380
+ this.loaded = false; // for ajax
381
+ this.loading = false; // for ajax
382
+
383
+ this.visible = false;
384
+ this.waitingToShow = false;
385
+ this.waitingToHide = false;
386
+
387
+ this.lastPosition = {left: 0, top: 0};
388
+ this.dimensions = [ 100, 50 ]; // Just some initial values.
389
+
390
+ var options = {};
391
+ this.content = '';
392
+
393
+ if (typeof(arguments[2]) == 'object') {options = Object.clone(arguments[2]);}
394
+ else if (typeof(arguments[3]) == 'object') {this.setContent(arguments[2]);options = Object.clone(arguments[3]);}
395
+ else if (typeof(arguments[4]) == 'object') {this.setContent(arguments[2]);options = Object.clone(arguments[4]);options.title = arguments[3];}
396
+ else {
397
+ if (Object.isString(arguments[2]) || Object.isFunction(arguments[2])) this.setContent(arguments[2]);
398
+ if (Object.isString(arguments[3])) options.title = arguments[3];
399
+ }
400
+
401
+ // Use the type of the html event (eg: onclick="") if called in an event.
402
+ if (!options.showOn && evt) options.showOn = evt.type;
403
+
404
+ // If the url of an Ajax request is not set, get it from the link it's attached to.
405
+ if (options.ajax && !options.ajax.url) {
406
+ if (this.triggerElement.tagName.toLowerCase() == 'a') {
407
+ if (typeof(options.ajax) != 'object') options.ajax = { };
408
+ options.ajax.url = this.triggerElement.href;
409
+ } else {options.ajax = false;}
410
+ }
411
+
412
+ // If the event is 'click', no point in following a link
413
+ if (options.showOn == 'click' && this.triggerElement.tagName.toLowerCase() == 'a') {if (evt) {evt.stop();}this.triggerElement.observe('click', function(e) {e.stop();});}
414
+
415
+
416
+ options.style || (options.style = Opentip.defaultStyle);
417
+
418
+ var styleOptions = Object.extend({ }, Opentip.styles.standard); // Copy all standard options.
419
+ if (options.style != 'standard') Object.extend(styleOptions, Opentip.styles[options.style]);
420
+
421
+ options = Object.extend(styleOptions, options);
422
+
423
+
424
+ options.target && (options.fixed = true);
425
+
426
+
427
+ if (options.stem === true) options.stem = options.tipJoint;
428
+ if (options.target === true) options.target = this.triggerElement;
429
+ else if (options.target) options.target = $(options.target);
430
+
431
+
432
+ this.currentStemPosition = options.stem;
433
+
434
+
435
+ if (options.delay === null) {
436
+ if (options.showOn == 'mouseover') options.delay = 0.2;
437
+ else options.delay = 0
438
+ }
439
+
440
+ if (Opentip.useScriptaculousTransitions) {
441
+ if (options.showEffect && ! Effect[options.showEffect.ot_ucfirst()]) {
442
+ this.debug('Using fallback show effect "' + options.fallbackShowEffect + '" instead of "' + options.showEffect + '"');
443
+ options.showEffect = options.fallbackShowEffect;
444
+ }
445
+ if (options.hideEffect && ! Effect[options.hideEffect.ot_ucfirst()]) {
446
+ this.debug('Using fallback hide effect "' + options.fallbackHideEffect + '" instead of "' + options.hideEffect + '"');
447
+ options.hideEffect = options.fallbackHideEffect;
448
+ }
449
+ }
450
+
451
+ if (options.targetJoint == null) {
452
+ options.targetJoint = [];
453
+ options.targetJoint[0] = options.tipJoint[0] == 'left' ? 'right' : (options.tipJoint[0] == 'right' ? 'left' : 'center');
454
+ options.targetJoint[1] = options.tipJoint[1] == 'top' ? 'bottom' : (options.tipJoint[1] == 'bottom' ? 'top' : 'middle');
455
+ }
456
+
457
+ this.options = options;
458
+
459
+ this.options.showTriggerElementsWhenHidden = [];
460
+
461
+ if (this.options.showOn && this.options.showOn != 'creation') {
462
+ this.options.showTriggerElementsWhenHidden.push({element: this.triggerElement, event: this.options.showOn});
463
+ }
464
+
465
+ this.options.showTriggerElementsWhenVisible = [];
466
+
467
+
468
+ this.options.hideTriggerElements = [];
469
+ },
470
+ /**
471
+ * This builds the container, and sets the correct hide trigger.
472
+ * Since it's a problem for IE to create elements when the page is not fully
473
+ * loaded, this function has to be posponed until the website is fully loaded.
474
+ *
475
+ * This function also activates the tooltip.
476
+ **/
477
+ create: function(evt) {
478
+ this.buildContainer();
479
+
480
+ if (this.options.hideTrigger) {
481
+ var hideOnEvent = null;
482
+ var hideTriggerElement = null;
483
+
484
+ if (!(this.options.hideTrigger instanceof Array)) {
485
+ this.options.hideTrigger = [this.options.hideTrigger];
486
+ }
487
+
488
+ this.options.hideTrigger.each(function(hideTrigger, i) {
489
+
490
+ var hideOnOption = (this.options.hideOn instanceof Array) ? this.options.hideOn[i] : this.options.hideOn;
491
+
492
+ switch (hideTrigger) {
493
+ case 'trigger':
494
+ hideOnEvent = hideOnOption ? hideOnOption : 'mouseout';
495
+ hideTriggerElement = this.triggerElement;
496
+ break;
497
+ case 'tip':
498
+ hideOnEvent = hideOnOption ? hideOnOption : 'mouseover';
499
+ hideTriggerElement = this.container;
500
+ break;
501
+ case 'target':
502
+ hideOnEvent = hideOnOption ? hideOnOption : 'mouseover';
503
+ hideTriggerElement = this.options.target;
504
+ break;
505
+ case 'closeButton':break;
506
+ default:
507
+ hideOnEvent = hideOnOption ? hideOnOption : 'mouseover';
508
+ hideTriggerElement = $(hideTrigger);
509
+ break;
510
+ }
511
+ if (hideTriggerElement) {
512
+ this.options.hideTriggerElements.push({element: hideTriggerElement, event: hideOnEvent});
513
+ if (hideOnEvent == 'mouseout') {
514
+ // When the hide trigger is mouseout, we have to attach a mouseover trigger to that element, so the tooltip doesn't disappear when
515
+ // hovering child elements. (Hovering children fires a mouseout mouseover event)
516
+ this.options.showTriggerElementsWhenVisible.push({element: hideTriggerElement, event: 'mouseover'});
517
+ }
518
+ }
519
+ }.bind(this));
520
+ }
521
+
522
+ this.activate();
523
+
524
+ if (evt || this.options.showOn == 'creation') this.show(evt);
525
+ },
526
+ activate: function() {
527
+ this.bound = {};
528
+ this.bound.doShow = this.doShow.bindAsEventListener(this);
529
+ this.bound.show = this.show.bindAsEventListener(this);
530
+ this.bound.doHide = this.doHide.bindAsEventListener(this);
531
+ this.bound.hide = this.hide.bindAsEventListener(this);
532
+ this.bound.position = this.position.bindAsEventListener(this);
533
+
534
+ if (this.options.showEffect || this.options.hideEffect) this.queue = {limit: 1, position: 'end', scope: this.container.identify()};
535
+
536
+ // The order is important here! Do not reverse.
537
+ this.setupObserversForReallyHiddenTip();
538
+ this.setupObserversForHiddenTip();
539
+ },
540
+ deactivate: function() {
541
+ this.debug('Deactivating tooltip.');
542
+ this.doHide();
543
+ this.setupObserversForReallyHiddenTip();
544
+ },
545
+ buildContainer: function() {
546
+ this.container = $(Opentip.element('div', {className: 'ot-container ot-completely-hidden style-' + this.options.className + (this.options.ajax ? ' ot-loading' : '') + (this.options.fixed ? ' ot-fixed' : '')}));
547
+ if (Opentip.useCss3Transitions) {
548
+ this.container.setCss3Style({'transitionDuration': '0s'}); // To make sure the initial state doesn't fade
549
+
550
+ this.container.addClassName('ot-css3');
551
+ if (this.options.showEffect) {
552
+ this.container.addClassName('ot-show-' + this.options.showEffect);
553
+ }
554
+ if (this.options.hideEffect) {
555
+ this.container.addClassName('ot-hide-' + this.options.hideEffect);
556
+ }
557
+ }
558
+ if (Opentip.useScriptaculousTransitions) this.container.setStyle({display: 'none'});
559
+ },
560
+ buildElements: function() {
561
+ var stemCanvas;
562
+ var closeButtonCanvas;
563
+ if (this.options.stem) {
564
+ var stemOffset = '-' + this.options.stemSize + 'px';
565
+ this.container.appendChild(Opentip.element('div', {className: 'stem-container ' + this.options.stem[0] + ' ' + this.options.stem[1]}, stemCanvas = Opentip.element('canvas', {className: 'stem'})));
566
+ }
567
+ var self = this;
568
+ var content = [];
569
+ var headerContent = [];
570
+ if (this.options.title) headerContent.push(Opentip.element('div', {className: 'title'}, this.options.title));
571
+
572
+ content.push(Opentip.element('div', {className: 'header'}, headerContent));
573
+ content.push($(Opentip.element('div', {className: 'content'}))); // Will be updated by updateElementContent()
574
+ if (this.options.ajax) {content.push($(Opentip.element('div', {className: 'loadingIndication'}, Opentip.element('span', 'Loading...'))));}
575
+ this.tooltipElement = $(Opentip.element('div', {className: 'opentip'}, content));
576
+
577
+ this.container.appendChild(this.tooltipElement);
578
+
579
+ var buttons = this.container.appendChild(Opentip.element('div', {className: 'ot-buttons'}));
580
+ var drawCloseButton = false;
581
+ if (this.options.hideTrigger && this.options.hideTrigger.include('closeButton')) {
582
+ buttons.appendChild(Opentip.element('a', {href: 'javascript:undefined', className: 'close'}, closeButtonCanvas = Opentip.element('canvas', { className: 'canvas' })));
583
+ // The canvas has to have a className assigned, because IE < 9 doesn't know the element, and won't assign any css to it.
584
+ drawCloseButton = true;
585
+ }
586
+
587
+ if (Opentip.useIFrame()) this.iFrameElement = this.container.appendChild($(Opentip.element('iframe', {className: 'opentipIFrame', src: 'javascript:false;'})).setStyle({display: 'none', zIndex: 100}).setOpacity(0));
588
+
589
+ document.body.appendChild(this.container);
590
+
591
+ if (typeof G_vmlCanvasManager !== "undefined") {
592
+ if (stemCanvas) G_vmlCanvasManager.initElement(stemCanvas);
593
+ if (closeButtonCanvas) G_vmlCanvasManager.initElement(closeButtonCanvas);
594
+ }
595
+
596
+ if (drawCloseButton) this.drawCloseButton();
597
+ },
598
+ drawCloseButton: function() {
599
+ var canvasElement = this.container.down('.ot-buttons canvas');
600
+ var containerElement = this.container.down('.ot-buttons .close');
601
+ var size = parseInt(containerElement.getStyle('width')) || 20; // Opera 10 has a bug here.. it seems to never get the width right.
602
+
603
+ var crossColor = canvasElement.getStyle('color');
604
+ if ( ! crossColor || crossColor == 'transparent') crossColor = 'white';
605
+
606
+ var backgroundColor = canvasElement.getStyle('backgroundColor');
607
+ if ( ! backgroundColor || backgroundColor == 'transparent') backgroundColor = 'rgba(0, 0, 0, 0.2)';
608
+ canvasElement.setStyle({backgroundColor: 'transparent'});
609
+
610
+ canvasElement.width = size;
611
+ canvasElement.height = size;
612
+
613
+ var ctx = canvasElement.getContext('2d');
614
+
615
+ ctx.clearRect (0, 0, size, size);
616
+
617
+ ctx.beginPath();
618
+
619
+ var padding = size / 2.95;
620
+ ctx.fillStyle = backgroundColor;
621
+ ctx.lineWidth = size / 5.26;
622
+ ctx.strokeStyle = crossColor;
623
+ ctx.lineCap = 'round';
624
+
625
+ ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2, false);
626
+ ctx.fill();
627
+
628
+ ctx.beginPath();
629
+ ctx.moveTo(padding, padding);
630
+ ctx.lineTo(size - padding, size - padding);
631
+ ctx.stroke();
632
+
633
+ ctx.beginPath();
634
+ ctx.moveTo(size - padding, padding);
635
+ ctx.lineTo(padding, size - padding);
636
+ ctx.stroke();
637
+
638
+ },
639
+ /**
640
+ * Sets the content of the tooltip.
641
+ * This can be a function or a string. The function will be executed, and the
642
+ * result used as new content of the tooltip.
643
+ *
644
+ * If the tooltip is visible, this function calls updateElementContent()
645
+ **/
646
+ setContent: function(content) {
647
+ this.content = content;
648
+ if (this.visible) this.updateElementContent();
649
+ },
650
+ /**
651
+ * Actually updates the html element with the content.
652
+ * This function also evaluates the content function, if content is a function.
653
+ **/
654
+ updateElementContent: function() {
655
+ var contentDiv = this.container.down('.content');
656
+ if (contentDiv) {
657
+
658
+ if (Object.isFunction(this.content)) {
659
+ this.debug('Executing content function.');
660
+ this.content = this.content(this);
661
+ }
662
+
663
+ contentDiv.update(this.options.escapeHtml ? this.content.escapeHTML() : this.content);
664
+ }
665
+ this.storeAndFixDimensions();
666
+ },
667
+ storeAndFixDimensions: function() {
668
+ this.container.setStyle({width: 'auto', left: '0px', top: '0px'});
669
+ this.dimensions = this.container.getDimensions();
670
+ this.container.setStyle({width: this.dimensions.width + 'px', left: this.lastPosition.left + 'px', top: this.lastPosition.top + 'px'});
671
+ },
672
+ destroyAllElements: function() {if (this.container) this.container.remove();},
673
+ clearShowTimeout: function() {window.clearTimeout(this.timeoutId);},
674
+ clearHideTimeout: function() {window.clearTimeout(this.hideTimeoutId);},
675
+ clearTimeouts: function() {
676
+ window.clearTimeout(this.visibilityStateTimeoutId);
677
+ this.clearShowTimeout();
678
+ this.clearHideTimeout();
679
+ },
680
+ /** Gets called only when doShow() is called, not when show() is called **/
681
+ setupObserversForReallyVisibleTip: function() {
682
+ this.options.showTriggerElementsWhenVisible.each(function(pair) {$(pair.element).observe(pair.event, this.bound.show);}, this);
683
+ },
684
+ /** Gets only called when show() is called. show() might not really result in showing the tooltip, because there may
685
+ be another trigger that calls hide() directly after. **/
686
+ setupObserversForVisibleTip: function() {
687
+ this.options.hideTriggerElements.each(function(pair) {$(pair.element).observe(pair.event, this.bound.hide);}, this);
688
+ this.options.showTriggerElementsWhenHidden.each(function(pair) {$(pair.element).stopObserving(pair.event, this.bound.show);}, this);
689
+ Event.observe(document.onresize ? document : window, "resize", this.bound.position);
690
+ Event.observe(window, "scroll", this.bound.position);
691
+ },
692
+ /** Gets called only when doHide() is called. */
693
+ setupObserversForReallyHiddenTip: function() {
694
+ this.options.showTriggerElementsWhenVisible.each(function(pair) {$(pair.element).stopObserving(pair.event, this.bound.show);}, this);
695
+ },
696
+ /** Gets called everytime hide() is called. See setupObserversForVisibleTip for more info **/
697
+ setupObserversForHiddenTip: function() {
698
+ this.options.showTriggerElementsWhenHidden.each(function(pair) {$(pair.element).observe(pair.event, this.bound.show);}, this);
699
+ this.options.hideTriggerElements.each(function(pair) {$(pair.element).stopObserving(pair.event, this.bound.hide);}, this);
700
+ Event.stopObserving(document.onresize ? document : window, "resize", this.bound.position);
701
+ Event.stopObserving(window, "scroll", this.bound.position);
702
+ },
703
+ /**
704
+ * The show function only schedules the tooltip to show. (If there is a delay, this function will generate the timer)
705
+ * The actual function to show the tooltip is doShow().
706
+ **/
707
+ show: function(evt) {
708
+ this.abortHiding();
709
+ if (this.visible) return;
710
+
711
+ this.debug('Showing in ' + this.options.delay + 's.');
712
+
713
+ if (this.options.group) Tips.abortShowingGroup(this.options.group);
714
+
715
+ this.waitingToShow = true;
716
+
717
+ // Even though it is not yet visible, I already attach the observers, so the tooltip won't show if a hideEvent is triggered.
718
+ this.setupObserversForVisibleTip();
719
+
720
+ // So the tooltip is positioned as soon as it shows.
721
+ this.followMousePosition();
722
+ this.position(evt);
723
+
724
+ if (!this.options.delay) this.bound.doShow(evt);
725
+ else this.timeoutId = this.bound.doShow.delay(this.options.delay);
726
+ },
727
+ // If the tip is waiting to show (and only then), this will abort it.
728
+ abortShowing: function() {
729
+ if (this.waitingToShow) {
730
+ this.debug('Aborting showing.');
731
+ this.clearTimeouts();
732
+ this.stopFollowingMousePosition();
733
+ this.waitingToShow = false;
734
+ this.setupObserversForHiddenTip();
735
+ }
736
+ },
737
+ /**
738
+ * Actually shows the tooltip. This function is called when any possible delay has expired.
739
+ **/
740
+ doShow: function() {
741
+ this.clearTimeouts();
742
+ if (this.visible) return;
743
+
744
+ // Thanks to Torsten Saam for this enhancement.
745
+ if ( ! this.triggerElementExists()) { this.deactivate(); return; }
746
+
747
+ this.debug('Showing!');
748
+
749
+ if (this.options.group) Tips.hideGroup(this.options.group);
750
+
751
+
752
+ this.visible = true;
753
+ this.waitingToShow = false;
754
+
755
+ if (!this.tooltipElement) this.buildElements();
756
+ this.updateElementContent();
757
+
758
+ if (this.options.ajax && !this.loaded) {this.loadAjax();}
759
+
760
+ this.searchAndActivateHideButtons();
761
+
762
+ this.ensureElement();
763
+ this.container.setStyle({zIndex: Opentip.lastZIndex += 1});
764
+
765
+ // The order is important here! Do not reverse.
766
+ this.setupObserversForReallyVisibleTip();
767
+ this.setupObserversForVisibleTip();
768
+
769
+ if (Opentip.useScriptaculousTransitions) {
770
+ if (this.options.showEffect || this.options.hideEffect) this.cancelEffects();
771
+
772
+ if (!this.options.showEffect || !this.container[this.options.showEffect]) this.container.show();
773
+ else this.container[this.options.showEffect]({duration: this.options.showEffectDuration, queue: this.queue, afterFinish: this.afterShowEffect.bind(this)});
774
+ if (Opentip.useIFrame()) this.iFrameElement.show();
775
+ }
776
+
777
+ this.position();
778
+
779
+ this.container.removeClassName('ot-hidden').addClassName('ot-becoming-visible');
780
+
781
+
782
+ /**
783
+ * The next lines may seem a bit weird. I ran into some bizarre opera problems
784
+ * while implementing the switch of the different states.
785
+ *
786
+ * This is what's happening here:
787
+ *
788
+ * I wanted to just remove ot-completely-hidden, and add ot-becoming-visible
789
+ * (so the tip has the style it should have when it appears) and then switch
790
+ * ot-becoming-visible with ot-visible so the transition can take place.
791
+ * I then setup a timer to set ot-completely-visible when appropriate.
792
+ *
793
+ * I ran into problems with opera, which showed the tip for a frame because
794
+ * apparently the -o-transforms are slower then just setting display: none
795
+ * (or something...)
796
+ *
797
+ * So I have to 1) set ot-becoming-visible first, so the tip has the appropriate
798
+ * CSS definitions set, 2) defer the removal of ot-completely-hidden, so it's
799
+ * not invisible anymore, and 3) defer the rest of the process (setting ot-visible
800
+ * and stuff) so the transition takes place.
801
+ */
802
+
803
+ var startShowEffect = function() {
804
+ if (Opentip.useCss3Transitions) {
805
+ this.container.setCss3Style({'transitionDuration': this.options.showEffectDuration + 's'});
806
+ }
807
+
808
+ this.container.removeClassName('ot-becoming-visible').addClassName('ot-visible');
809
+ if (this.options.showEffect && this.options.showEffectDuration) {
810
+ this.visibilityStateTimeoutId = (function() {this.removeClassName('ot-visible').addClassName('ot-completely-visible');}).bind(this.container).delay(this.options.showEffectDuration);
811
+ }
812
+ else {
813
+ this.container.removeClassName('ot-visible').addClassName('ot-completely-visible');
814
+ }
815
+
816
+ this.activateFirstInput();
817
+ };
818
+
819
+
820
+ (function() {
821
+ this.container.removeClassName('ot-completely-hidden');
822
+ (startShowEffect).bind(this).defer(); // Has to be deferred, so the div has the class ot-becoming-visible.
823
+ }).bind(this).defer();
824
+
825
+
826
+
827
+ },
828
+ loadAjax: function() {
829
+ if (this.loading) return;
830
+ this.loading = true;
831
+ this.container.addClassName('ot-loading');
832
+
833
+ this.debug('Loading content from ' + this.options.ajax.url + '.');
834
+
835
+ new Ajax.Request(this.options.ajax.url,
836
+ Object.extend({
837
+ onComplete: function() {
838
+ this.container.removeClassName('ot-loading');
839
+ this.loaded = true;
840
+ this.loading = false;
841
+ this.updateElementContent();
842
+ this.searchAndActivateHideButtons();
843
+ this.activateFirstInput();
844
+ this.position();
845
+ }.bind(this),
846
+ onSuccess: function(transport) {
847
+ this.debug('Loading successfull.');
848
+ this.content = transport.responseText;
849
+ }.bind(this),
850
+ onFailure: function() {
851
+ this.debug('There was a problem downloading the file.');
852
+ this.options.escapeHtml = false;
853
+ this.content = '<a class="close">There was a problem downloading the content.</a>';
854
+ }.bind(this)}, this.options.ajax.options || {}));
855
+ },
856
+ afterShowEffect: function() {
857
+ this.activateFirstInput();
858
+ this.position();
859
+ },
860
+ activateFirstInput: function() {
861
+ // TODO: check if there is a simple way of finding EITHER an input OR a textarea.
862
+ var input = this.container.down('input');
863
+ var textarea = this.container.down('textarea');
864
+ if (input) {input.focus();}
865
+ else if (textarea) textarea.focus();
866
+ },
867
+ searchAndActivateHideButtons: function() {
868
+ if (!this.options.hideTrigger || this.options.hideTrigger.include('closeButton')) {
869
+ this.options.hideTriggerElements = [];
870
+ this.container.select('.close').each(function(el) {
871
+ this.options.hideTriggerElements.push({element: el, event: 'click'});
872
+ }, this);
873
+ if (this.visible) this.setupObserversForVisibleTip();
874
+ }
875
+ },
876
+ hide: function(afterFinish) {
877
+ this.abortShowing();
878
+ if (!this.visible) return;
879
+
880
+ this.debug('Hiding in ' + this.options.hideDelay + 's.');
881
+
882
+ this.waitingToHide = true;
883
+
884
+ // We start observing even though it is not yet hidden, so the tooltip does not disappear when a showEvent is triggered.
885
+ this.setupObserversForHiddenTip();
886
+
887
+ this.hideTimeoutId = this.bound.doHide.delay(this.options.hideDelay, afterFinish); // hide has to be delayed because when hovering children a mouseout is registered.
888
+ },
889
+ abortHiding: function() {
890
+ if (this.waitingToHide) {
891
+ this.debug('Aborting hiding.');
892
+ this.clearTimeouts();
893
+ this.waitingToHide = false;
894
+ this.setupObserversForVisibleTip();
895
+ }
896
+ },
897
+ doHide: function(afterFinish) {
898
+ this.clearTimeouts();
899
+ if (!this.visible) return;
900
+
901
+ this.debug('Hiding!');
902
+
903
+ this.visible = false;
904
+
905
+ this.waitingToHide = false;
906
+
907
+ this.deactivateElementEnsurance();
908
+
909
+ // The order is important here! Do not reverse.
910
+ this.setupObserversForReallyHiddenTip();
911
+ this.setupObserversForHiddenTip();
912
+
913
+ if (!this.options.fixed) this.stopFollowingMousePosition();
914
+
915
+ if (Opentip.useScriptaculousTransitions) {
916
+ if (this.options.showEffect || this.options.hideEffect) this.cancelEffects();
917
+
918
+ if (!this.options.hideEffect || !this.container[this.options.hideEffect]) this.container.hide();
919
+ else {
920
+ var effectOptions = {duration: this.options.hideEffectDuration, queue: this.queue};
921
+ if(afterFinish && Object.isFunction(afterFinish)) effectOptions.afterFinish = afterFinish;
922
+ this.container[this.options.hideEffect](effectOptions);
923
+ }
924
+ if (Opentip.useIFrame()) this.iFrameElement.hide();
925
+ }
926
+
927
+ if (Opentip.useCss3Transitions) {
928
+ this.container.setCss3Style({'transitionDuration': this.options.hideEffectDuration + 's'});
929
+ }
930
+
931
+ this.container.removeClassName('ot-visible').removeClassName('ot-completely-visible').addClassName('ot-hidden');
932
+ if (this.options.hideEffect && this.options.hideEffectDuration) {
933
+ this.visibilityStateTimeoutId = (function() {
934
+ this.setCss3Style({'transitionDuration': '0s'});
935
+ this.removeClassName('ot-hidden').addClassName('ot-completely-hidden');
936
+ }).bind(this.container).delay(this.options.showEffectDuration);
937
+ }
938
+ else {
939
+ this.container.removeClassName('ot-hidden').addClassName('ot-completely-hidden');
940
+ }
941
+
942
+ },
943
+ cancelEffects: function() {Effect.Queues.get(this.queue.scope).invoke('cancel');},
944
+ followMousePosition: function() {if (!this.options.fixed) $(document.body).observe('mousemove', this.bound.position);},
945
+ stopFollowingMousePosition: function() {if (!this.options.fixed) $(document.body).stopObserving('mousemove', this.bound.position);},
946
+ positionsEqual: function(position1, position2) {
947
+ return (position1.left == position2.left && position1.top == position2.top);
948
+ },
949
+ position: function(evt) {
950
+ var evt = evt || this.lastEvt;
951
+
952
+ this.currentStemPosition = this.options.stem; // This gets reset by ensureViewportContainment if necessary.
953
+ var position = this.ensureViewportContainment(evt, this.getPosition(evt));
954
+ if (this.positionsEqual(position, this.lastPosition)) {
955
+ this.positionStem();
956
+ return;
957
+ }
958
+
959
+ this.lastPosition = position;
960
+ if (position) {
961
+ var style = {'left': position.left + 'px', 'top': position.top + 'px'};
962
+ this.container.setStyle(style);
963
+ if (Opentip.useIFrame() && this.iFrameElement) {
964
+ this.iFrameElement.setStyle({width: this.container.getWidth() + 'px', height: this.container.getHeight() + 'px'});
965
+ }
966
+
967
+ /**
968
+ * Following is a redraw fix, because I noticed some drawing errors in some browsers when tooltips where overlapping.
969
+ */
970
+ var container = this.container;
971
+ (function() {
972
+ container.style.visibility = "hidden"; // I chose visibility instead of display so that I don't interfere with appear/disappear effects.
973
+ var redrawFix = container.offsetHeight;
974
+ container.style.visibility = "visible";
975
+ }).defer();
976
+ }
977
+ this.positionStem();
978
+ },
979
+ getPosition: function(evt, tipJ, trgJ, stem) {
980
+ var tipJ = tipJ || this.options.tipJoint;
981
+ var trgJ = trgJ || this.options.targetJoint;
982
+
983
+ var position = {};
984
+
985
+ if (this.options.target) {
986
+ var tmp = this.options.target.cumulativeOffset();
987
+ position.left = tmp[0];
988
+ position.top = tmp[1];
989
+ if (trgJ[0] == 'right') {
990
+ // For wrapping inline elements, left + width does not give the right border, because left is where
991
+ // the element started, not its most left position.
992
+ if (typeof this.options.target.getBoundingClientRect != 'undefined') {
993
+ position.left = this.options.target.getBoundingClientRect().right + $(document.viewport).getScrollOffsets().left;
994
+ }
995
+ else {
996
+ position.left = position.left + this.options.target.getWidth();
997
+ }
998
+ }
999
+ else if (trgJ[0] == 'center') {position.left += Math.round(this.options.target.getWidth() / 2);}
1000
+ if (trgJ[1] == 'bottom') {position.top += this.options.target.getHeight();}
1001
+ else if (trgJ[1] == 'middle') {position.top += Math.round(this.options.target.getHeight() / 2);}
1002
+ } else {
1003
+ if (!evt) return; // No event passed, so returning.
1004
+ this.lastEvt = evt;
1005
+ position.left = Event.pointerX(evt);
1006
+ position.top = Event.pointerY(evt);
1007
+ }
1008
+
1009
+ if (this.options.autoOffset) {
1010
+ var stemSize = this.options.stem ? this.options.stemSize : 0;
1011
+ var offsetDistance = (stemSize && this.options.fixed) ? 2 : 10; // If there is as stem offsets dont need to be that big if fixed.
1012
+ var additionalHorizontal = (tipJ[1] == 'middle' && !this.options.fixed) ? 15 : 0;
1013
+ var additionalVertical = (tipJ[0] == 'center' && !this.options.fixed) ? 15 : 0;
1014
+ if (tipJ[0] == 'right') position.left -= offsetDistance + additionalHorizontal;
1015
+ else if (tipJ[0] == 'left') position.left += offsetDistance + additionalHorizontal;
1016
+ if (tipJ[1] == 'bottom') position.top -= offsetDistance + additionalVertical;
1017
+ else if (tipJ[1] == 'top') position.top += offsetDistance + additionalVertical;
1018
+
1019
+ if (stemSize) {
1020
+ var stem = stem || this.options.stem;
1021
+ if (stem[0] == 'right') position.left -= stemSize;
1022
+ else if (stem[0] == 'left') position.left += stemSize;
1023
+ if (stem[1] == 'bottom') position.top -= stemSize;
1024
+ else if (stem[1] == 'top') position.top += stemSize;
1025
+ }
1026
+ }
1027
+ position.left += this.options.offset[0];
1028
+ position.top += this.options.offset[1];
1029
+
1030
+ if (tipJ[0] == 'right') {position.left -= this.container.getWidth();}
1031
+ if (tipJ[0] == 'center') {position.left -= Math.round(this.container.getWidth()/2);}
1032
+ if (tipJ[1] == 'bottom') {position.top -= this.container.getHeight();}
1033
+ if (tipJ[1] == 'middle') {position.top -= Math.round(this.container.getHeight()/2);}
1034
+
1035
+ return position;
1036
+ },
1037
+ ensureViewportContainment: function(evt, position) {
1038
+ // Sometimes the element is theoretically visible, but an effect is not yet showing it.
1039
+ // So the calculation of the offsets is incorrect sometimes, which results in faulty repositioning.
1040
+ if (!this.visible) return position;
1041
+
1042
+ var sticksOut = [ this.sticksOutX(position), this.sticksOutY(position) ];
1043
+ if (!sticksOut[0] && !sticksOut[1]) return position;
1044
+
1045
+ var tipJ = this.options.tipJoint.clone();
1046
+ var trgJ = this.options.targetJoint.clone();
1047
+
1048
+ var viewportScrollOffset = $(document.viewport).getScrollOffsets();
1049
+ var dimensions = this.dimensions;
1050
+ var viewportOffset = {left: position.left - viewportScrollOffset.left, top: position.top - viewportScrollOffset.top};
1051
+ var viewportDimensions = document.viewport.getDimensions();
1052
+ var reposition = false;
1053
+
1054
+ if (viewportDimensions.width >= dimensions.width) {
1055
+ if (viewportOffset.left < 0) {
1056
+ reposition = true;
1057
+ tipJ[0] = 'left';
1058
+ if (this.options.target && trgJ[0] == 'left') {trgJ[0] = 'right';}
1059
+ }
1060
+ else if (viewportOffset.left + dimensions.width > viewportDimensions.width) {
1061
+ reposition = true;
1062
+ tipJ[0] = 'right';
1063
+ if (this.options.target && trgJ[0] == 'right') {trgJ[0] = 'left';}
1064
+ }
1065
+ }
1066
+
1067
+ if (viewportDimensions.height >= dimensions.height) {
1068
+ if (viewportOffset.top < 0) {
1069
+ reposition = true;
1070
+ tipJ[1] = 'top';
1071
+ if (this.options.target && trgJ[1] == 'top') {trgJ[1] = 'bottom';}
1072
+ }
1073
+ else if (viewportOffset.top + dimensions.height > viewportDimensions.height) {
1074
+ reposition = true;
1075
+ tipJ[1] = 'bottom';
1076
+ if (this.options.target && trgJ[1] == 'bottom') {trgJ[1] = 'top';}
1077
+ }
1078
+ }
1079
+ if (reposition) {
1080
+ var newPosition = this.getPosition(evt, tipJ, trgJ, tipJ);
1081
+ var newSticksOut = [ this.sticksOutX(newPosition), this.sticksOutY(newPosition) ];
1082
+ var revertedCount = 0;
1083
+ for (var i = 0; i <=1; i ++) {
1084
+ if (newSticksOut[i] && newSticksOut[i] != sticksOut[i]) {
1085
+ // The tooltip changed sides, but now is sticking out the other side of the window.
1086
+ // If its still sticking out, but on the same side, it's ok. At least, it sticks out less.
1087
+ revertedCount ++;
1088
+ tipJ[i] = this.options.tipJoint[i];
1089
+ if (this.options.target) {trgJ[i] = this.options.targetJoint[i];}
1090
+ }
1091
+ }
1092
+ if (revertedCount < 2) {
1093
+ this.currentStemPosition = tipJ;
1094
+ return this.getPosition(evt, tipJ, trgJ, tipJ);
1095
+ }
1096
+ }
1097
+ return position;
1098
+ },
1099
+ sticksOut: function(position) {
1100
+ return this.sticksOutX(position) || this.sticksOutY(position);
1101
+ },
1102
+ /**
1103
+ * return 1 for left 2 for right
1104
+ */
1105
+ sticksOutX: function(position) {
1106
+ var viewportScrollOffset = $(document.viewport).getScrollOffsets();
1107
+ var viewportOffset = {left: position.left - viewportScrollOffset.left, top: position.top - viewportScrollOffset.top};
1108
+ if (viewportOffset.left < 0) return Opentip.STICKS_OUT_LEFT;
1109
+ if (viewportOffset.left + this.dimensions.width > document.viewport.getDimensions().width) {return Opentip.STICKS_OUT_RIGHT;}
1110
+ },
1111
+ /**
1112
+ * return 1 for top 2 for bottom
1113
+ */
1114
+ sticksOutY: function(position) {
1115
+ var viewportScrollOffset = $(document.viewport).getScrollOffsets();
1116
+ var viewportOffset = {left: position.left - viewportScrollOffset.left, top: position.top - viewportScrollOffset.top};
1117
+ if (viewportOffset.top < 0) return Opentip.STICKS_OUT_TOP;
1118
+ if (viewportOffset.top + this.dimensions.height > document.viewport.getDimensions().height) return Opentip.STICKS_OUT_BOTTOM;
1119
+ },
1120
+ getStemCanvas: function() {
1121
+ return this.container.down('.stem');
1122
+ },
1123
+ stemPositionsEqual: function(position1, position2) {
1124
+ return (position1 && position2 && position1[0] == position2[0] && position1[1] == position2[1]);
1125
+ },
1126
+ positionStem: function() {
1127
+ // Position stem
1128
+ if (this.options.stem) {
1129
+
1130
+ var canvasElement = this.getStemCanvas();
1131
+
1132
+ if (canvasElement && !this.stemPositionsEqual(this.lastStemPosition, this.currentStemPosition)) {
1133
+
1134
+ this.debug('Setting stem style');
1135
+
1136
+ this.lastStemPosition = this.currentStemPosition;
1137
+
1138
+ var stemPosition = Opentip.sanitizePosition(this.currentStemPosition);
1139
+ var stemSize = this.options.stemSize;
1140
+
1141
+ var rotationRad = stemPosition * Math.PI / 4; // Every number means 45deg
1142
+
1143
+ var baseThikness = Math.round(stemSize * 1.5);
1144
+
1145
+ var realDim = {w: baseThikness, h: stemSize};
1146
+
1147
+ var isCorner = false;
1148
+ if (stemPosition % 2 == 1) {
1149
+ // Corner
1150
+ isCorner = true;
1151
+ var additionalWidth = Math.round(0.707106781 * baseThikness); // 0.707106781 == sqrt(2) / 2 to calculate the adjacent leg of the triangle
1152
+ realDim = {w: stemSize + additionalWidth, h: stemSize + additionalWidth};
1153
+ }
1154
+
1155
+ var drawDim = Object.clone(realDim); // The drawDim is so that I can draw without takin the rotation into calculation
1156
+
1157
+ if (stemPosition == Opentip.position.left || stemPosition == Opentip.position.right) {
1158
+ // The canvas has to be rotated
1159
+ realDim.h = drawDim.w;
1160
+ realDim.w = drawDim.h;
1161
+ }
1162
+
1163
+
1164
+ var stemColor = canvasElement.getStyle('color') || 'black';
1165
+
1166
+
1167
+ canvasElement.width = realDim.w;
1168
+ canvasElement.height = realDim.h;
1169
+
1170
+ // Now draw the stem.
1171
+ var ctx = canvasElement.getContext('2d');
1172
+
1173
+ ctx.clearRect (0, 0, canvasElement.width, canvasElement.height);
1174
+ ctx.beginPath();
1175
+
1176
+ ctx.fillStyle = stemColor;
1177
+
1178
+ ctx.save();
1179
+
1180
+ ctx.translate(realDim.w / 2, realDim.h / 2);
1181
+ var rotations = Math.floor(stemPosition / 2);
1182
+ ctx.rotate(rotations * Math.PI / 2);
1183
+ if (realDim.w == drawDim.w) { // This is a real hack because I don't know how to reset to 0,0
1184
+ ctx.translate(-realDim.w / 2, -realDim.h / 2);
1185
+ }
1186
+ else {
1187
+ ctx.translate(-realDim.h / 2, -realDim.w / 2);
1188
+ }
1189
+
1190
+ if (isCorner) {
1191
+ ctx.moveTo(additionalWidth, drawDim.h);
1192
+ ctx.lineTo(drawDim.w, 0);
1193
+ ctx.lineTo(0, drawDim.h - additionalWidth);
1194
+ }
1195
+ else {
1196
+ ctx.moveTo(drawDim.w / 2 - baseThikness / 2, drawDim.h);
1197
+ ctx.lineTo(drawDim.w / 2, 0);
1198
+ ctx.lineTo(drawDim.w / 2 + baseThikness / 2, drawDim.h);
1199
+ }
1200
+ ctx.fill();
1201
+ ctx.restore();
1202
+
1203
+
1204
+ var style = {width: realDim.w + 'px', height: realDim.h + 'px', left: '', right: '', top: '', bottom: ''};
1205
+
1206
+ switch (stemPosition) {
1207
+ case Opentip.position.top:
1208
+ style.top = - realDim.h + 'px';
1209
+ style.left = - Math.round(realDim.w / 2) + 'px';
1210
+ break;
1211
+ case Opentip.position.right:
1212
+ style.top = - Math.round(realDim.h / 2) + 'px';
1213
+ style.left = 0;
1214
+ break;
1215
+ case Opentip.position.bottom:
1216
+ style.top = 0;
1217
+ style.left = - Math.round(realDim.w / 2) + 'px';
1218
+ break;
1219
+ case Opentip.position.left:
1220
+ style.top = - Math.round(realDim.h / 2) + 'px';
1221
+ style.left = - realDim.w + 'px';
1222
+ break;
1223
+ case Opentip.position.topRight:
1224
+ style.top = - stemSize + 'px';
1225
+ style.left = - additionalWidth + 'px';
1226
+ break;
1227
+ case Opentip.position.bottomRight:
1228
+ style.top = - additionalWidth + 'px';
1229
+ style.left = - additionalWidth + 'px';
1230
+ break;
1231
+ case Opentip.position.bottomLeft:
1232
+ style.top = - additionalWidth + 'px';
1233
+ style.left = - stemSize + 'px';
1234
+ break;
1235
+ case Opentip.position.topLeft:
1236
+ style.top = - stemSize + 'px';
1237
+ style.left = - stemSize + 'px';
1238
+ break;
1239
+ default:
1240
+ throw 'Unknown stem position: ' + stemPosition;
1241
+ }
1242
+
1243
+ canvasElement.setStyle(style);
1244
+
1245
+ var stemContainer = canvasElement.up('.stem-container');
1246
+ stemContainer.removeClassName('left').removeClassName('right').removeClassName('center').removeClassName('top').removeClassName('bottom').removeClassName('middle');
1247
+
1248
+ switch (stemPosition) {
1249
+ case Opentip.position.top: case Opentip.position.topLeft: case Opentip.position.topRight:
1250
+ stemContainer.addClassName('top');
1251
+ break;
1252
+ case Opentip.position.bottom: case Opentip.position.bottomLeft: case Opentip.position.bottomRight:
1253
+ stemContainer.addClassName('bottom');
1254
+ break;
1255
+ default:
1256
+ stemContainer.addClassName('middle');
1257
+ break;
1258
+ }
1259
+ switch (stemPosition) {
1260
+ case Opentip.position.left: case Opentip.position.topLeft: case Opentip.position.bottomLeft:
1261
+ stemContainer.addClassName('left');
1262
+ break;
1263
+ case Opentip.position.right: case Opentip.position.topRight: case Opentip.position.bottomRight:
1264
+ stemContainer.addClassName('right');
1265
+ break;
1266
+ default:
1267
+ stemContainer.addClassName('center');
1268
+ break;
1269
+ }
1270
+
1271
+ }
1272
+
1273
+ }
1274
+ },
1275
+ triggerElementExists: function(element) {
1276
+ return this.triggerElement.parentNode && this.triggerElement.visible() && this.triggerElement.descendantOf(document.body);
1277
+ },
1278
+ ensureElementInterval: 1000, // In milliseconds, how often opentip should check for the existance of the element
1279
+ ensureElement: function() { // Regularely checks if the element is still in the dom.
1280
+ this.deactivateElementEnsurance();
1281
+ if ( ! this.triggerElementExists()) {this.deactivate();}
1282
+ this.ensureElementTimeoutId = setTimeout(this.ensureElement.bind(this), this.ensureElementInterval);
1283
+ },
1284
+ deactivateElementEnsurance: function() {clearTimeout(this.ensureElementTimeoutId);}
1285
+ });
js/mageho/sortproducts/prototype.js ADDED
@@ -0,0 +1,6081 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Prototype JavaScript framework, version 1.7
2
+ * (c) 2005-2010 Sam Stephenson
3
+ *
4
+ * Prototype is freely distributable under the terms of an MIT-style license.
5
+ * For details, see the Prototype web site: http://www.prototypejs.org/
6
+ *
7
+ *--------------------------------------------------------------------------*/
8
+
9
+ var Prototype = {
10
+
11
+ Version: '1.7',
12
+
13
+ Browser: (function(){
14
+ var ua = navigator.userAgent;
15
+ var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
16
+ return {
17
+ IE: !!window.attachEvent && !isOpera,
18
+ Opera: isOpera,
19
+ WebKit: ua.indexOf('AppleWebKit/') > -1,
20
+ Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
21
+ MobileSafari: /Apple.*Mobile/.test(ua)
22
+ }
23
+ })(),
24
+
25
+ BrowserFeatures: {
26
+ XPath: !!document.evaluate,
27
+
28
+ SelectorsAPI: !!document.querySelector,
29
+
30
+ ElementExtensions: (function() {
31
+ var constructor = window.Element || window.HTMLElement;
32
+ return !!(constructor && constructor.prototype);
33
+ })(),
34
+ SpecificElementExtensions: (function() {
35
+ if (typeof window.HTMLDivElement !== 'undefined')
36
+ return true;
37
+
38
+ var div = document.createElement('div'),
39
+ form = document.createElement('form'),
40
+ isSupported = false;
41
+
42
+ if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
43
+ isSupported = true;
44
+ }
45
+
46
+ div = form = null;
47
+
48
+ return isSupported;
49
+ })()
50
+ },
51
+
52
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
53
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
54
+
55
+ emptyFunction: function() { },
56
+
57
+ K: function(x) { return x }
58
+ };
59
+
60
+ if (Prototype.Browser.MobileSafari)
61
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
62
+ /* Based on Alex Arnell's inheritance implementation. */
63
+
64
+ var Class = (function() {
65
+
66
+ var IS_DONTENUM_BUGGY = (function(){
67
+ for (var p in { toString: 1 }) {
68
+ if (p === 'toString') return false;
69
+ }
70
+ return true;
71
+ })();
72
+
73
+ function subclass() {};
74
+ function create() {
75
+ var parent = null, properties = $A(arguments);
76
+ if (Object.isFunction(properties[0]))
77
+ parent = properties.shift();
78
+
79
+ function klass() {
80
+ this.initialize.apply(this, arguments);
81
+ }
82
+
83
+ Object.extend(klass, Class.Methods);
84
+ klass.superclass = parent;
85
+ klass.subclasses = [];
86
+
87
+ if (parent) {
88
+ subclass.prototype = parent.prototype;
89
+ klass.prototype = new subclass;
90
+ parent.subclasses.push(klass);
91
+ }
92
+
93
+ for (var i = 0, length = properties.length; i < length; i++)
94
+ klass.addMethods(properties[i]);
95
+
96
+ if (!klass.prototype.initialize)
97
+ klass.prototype.initialize = Prototype.emptyFunction;
98
+
99
+ klass.prototype.constructor = klass;
100
+ return klass;
101
+ }
102
+
103
+ function addMethods(source) {
104
+ var ancestor = this.superclass && this.superclass.prototype,
105
+ properties = Object.keys(source);
106
+
107
+ if (IS_DONTENUM_BUGGY) {
108
+ if (source.toString != Object.prototype.toString)
109
+ properties.push("toString");
110
+ if (source.valueOf != Object.prototype.valueOf)
111
+ properties.push("valueOf");
112
+ }
113
+
114
+ for (var i = 0, length = properties.length; i < length; i++) {
115
+ var property = properties[i], value = source[property];
116
+ if (ancestor && Object.isFunction(value) &&
117
+ value.argumentNames()[0] == "$super") {
118
+ var method = value;
119
+ value = (function(m) {
120
+ return function() { return ancestor[m].apply(this, arguments); };
121
+ })(property).wrap(method);
122
+
123
+ value.valueOf = method.valueOf.bind(method);
124
+ value.toString = method.toString.bind(method);
125
+ }
126
+ this.prototype[property] = value;
127
+ }
128
+
129
+ return this;
130
+ }
131
+
132
+ return {
133
+ create: create,
134
+ Methods: {
135
+ addMethods: addMethods
136
+ }
137
+ };
138
+ })();
139
+ (function() {
140
+
141
+ var _toString = Object.prototype.toString,
142
+ NULL_TYPE = 'Null',
143
+ UNDEFINED_TYPE = 'Undefined',
144
+ BOOLEAN_TYPE = 'Boolean',
145
+ NUMBER_TYPE = 'Number',
146
+ STRING_TYPE = 'String',
147
+ OBJECT_TYPE = 'Object',
148
+ FUNCTION_CLASS = '[object Function]',
149
+ BOOLEAN_CLASS = '[object Boolean]',
150
+ NUMBER_CLASS = '[object Number]',
151
+ STRING_CLASS = '[object String]',
152
+ ARRAY_CLASS = '[object Array]',
153
+ DATE_CLASS = '[object Date]',
154
+ NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
155
+ typeof JSON.stringify === 'function' &&
156
+ JSON.stringify(0) === '0' &&
157
+ typeof JSON.stringify(Prototype.K) === 'undefined';
158
+
159
+ function Type(o) {
160
+ switch(o) {
161
+ case null: return NULL_TYPE;
162
+ case (void 0): return UNDEFINED_TYPE;
163
+ }
164
+ var type = typeof o;
165
+ switch(type) {
166
+ case 'boolean': return BOOLEAN_TYPE;
167
+ case 'number': return NUMBER_TYPE;
168
+ case 'string': return STRING_TYPE;
169
+ }
170
+ return OBJECT_TYPE;
171
+ }
172
+
173
+ function extend(destination, source) {
174
+ for (var property in source)
175
+ destination[property] = source[property];
176
+ return destination;
177
+ }
178
+
179
+ function inspect(object) {
180
+ try {
181
+ if (isUndefined(object)) return 'undefined';
182
+ if (object === null) return 'null';
183
+ return object.inspect ? object.inspect() : String(object);
184
+ } catch (e) {
185
+ if (e instanceof RangeError) return '...';
186
+ throw e;
187
+ }
188
+ }
189
+
190
+ function toJSON(value) {
191
+ return Str('', { '': value }, []);
192
+ }
193
+
194
+ function Str(key, holder, stack) {
195
+ var value = holder[key],
196
+ type = typeof value;
197
+
198
+ if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
199
+ value = value.toJSON(key);
200
+ }
201
+
202
+ var _class = _toString.call(value);
203
+
204
+ switch (_class) {
205
+ case NUMBER_CLASS:
206
+ case BOOLEAN_CLASS:
207
+ case STRING_CLASS:
208
+ value = value.valueOf();
209
+ }
210
+
211
+ switch (value) {
212
+ case null: return 'null';
213
+ case true: return 'true';
214
+ case false: return 'false';
215
+ }
216
+
217
+ type = typeof value;
218
+ switch (type) {
219
+ case 'string':
220
+ return value.inspect(true);
221
+ case 'number':
222
+ return isFinite(value) ? String(value) : 'null';
223
+ case 'object':
224
+
225
+ for (var i = 0, length = stack.length; i < length; i++) {
226
+ if (stack[i] === value) { throw new TypeError(); }
227
+ }
228
+ stack.push(value);
229
+
230
+ var partial = [];
231
+ if (_class === ARRAY_CLASS) {
232
+ for (var i = 0, length = value.length; i < length; i++) {
233
+ var str = Str(i, value, stack);
234
+ partial.push(typeof str === 'undefined' ? 'null' : str);
235
+ }
236
+ partial = '[' + partial.join(',') + ']';
237
+ } else {
238
+ var keys = Object.keys(value);
239
+ for (var i = 0, length = keys.length; i < length; i++) {
240
+ var key = keys[i], str = Str(key, value, stack);
241
+ if (typeof str !== "undefined") {
242
+ partial.push(key.inspect(true)+ ':' + str);
243
+ }
244
+ }
245
+ partial = '{' + partial.join(',') + '}';
246
+ }
247
+ stack.pop();
248
+ return partial;
249
+ }
250
+ }
251
+
252
+ function stringify(object) {
253
+ return JSON.stringify(object);
254
+ }
255
+
256
+ function toQueryString(object) {
257
+ return $H(object).toQueryString();
258
+ }
259
+
260
+ function toHTML(object) {
261
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
262
+ }
263
+
264
+ function keys(object) {
265
+ if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
266
+ var results = [];
267
+ for (var property in object) {
268
+ if (object.hasOwnProperty(property)) {
269
+ results.push(property);
270
+ }
271
+ }
272
+ return results;
273
+ }
274
+
275
+ function values(object) {
276
+ var results = [];
277
+ for (var property in object)
278
+ results.push(object[property]);
279
+ return results;
280
+ }
281
+
282
+ function clone(object) {
283
+ return extend({ }, object);
284
+ }
285
+
286
+ function isElement(object) {
287
+ return !!(object && object.nodeType == 1);
288
+ }
289
+
290
+ function isArray(object) {
291
+ return _toString.call(object) === ARRAY_CLASS;
292
+ }
293
+
294
+ var hasNativeIsArray = (typeof Array.isArray == 'function')
295
+ && Array.isArray([]) && !Array.isArray({});
296
+
297
+ if (hasNativeIsArray) {
298
+ isArray = Array.isArray;
299
+ }
300
+
301
+ function isHash(object) {
302
+ return object instanceof Hash;
303
+ }
304
+
305
+ function isFunction(object) {
306
+ return _toString.call(object) === FUNCTION_CLASS;
307
+ }
308
+
309
+ function isString(object) {
310
+ return _toString.call(object) === STRING_CLASS;
311
+ }
312
+
313
+ function isNumber(object) {
314
+ return _toString.call(object) === NUMBER_CLASS;
315
+ }
316
+
317
+ function isDate(object) {
318
+ return _toString.call(object) === DATE_CLASS;
319
+ }
320
+
321
+ function isUndefined(object) {
322
+ return typeof object === "undefined";
323
+ }
324
+
325
+ extend(Object, {
326
+ extend: extend,
327
+ inspect: inspect,
328
+ toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
329
+ toQueryString: toQueryString,
330
+ toHTML: toHTML,
331
+ keys: Object.keys || keys,
332
+ values: values,
333
+ clone: clone,
334
+ isElement: isElement,
335
+ isArray: isArray,
336
+ isHash: isHash,
337
+ isFunction: isFunction,
338
+ isString: isString,
339
+ isNumber: isNumber,
340
+ isDate: isDate,
341
+ isUndefined: isUndefined
342
+ });
343
+ })();
344
+ Object.extend(Function.prototype, (function() {
345
+ var slice = Array.prototype.slice;
346
+
347
+ function update(array, args) {
348
+ var arrayLength = array.length, length = args.length;
349
+ while (length--) array[arrayLength + length] = args[length];
350
+ return array;
351
+ }
352
+
353
+ function merge(array, args) {
354
+ array = slice.call(array, 0);
355
+ return update(array, args);
356
+ }
357
+
358
+ function argumentNames() {
359
+ var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
360
+ .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
361
+ .replace(/\s+/g, '').split(',');
362
+ return names.length == 1 && !names[0] ? [] : names;
363
+ }
364
+
365
+ function bind(context) {
366
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
367
+ var __method = this, args = slice.call(arguments, 1);
368
+ return function() {
369
+ var a = merge(args, arguments);
370
+ return __method.apply(context, a);
371
+ }
372
+ }
373
+
374
+ function bindAsEventListener(context) {
375
+ var __method = this, args = slice.call(arguments, 1);
376
+ return function(event) {
377
+ var a = update([event || window.event], args);
378
+ return __method.apply(context, a);
379
+ }
380
+ }
381
+
382
+ function curry() {
383
+ if (!arguments.length) return this;
384
+ var __method = this, args = slice.call(arguments, 0);
385
+ return function() {
386
+ var a = merge(args, arguments);
387
+ return __method.apply(this, a);
388
+ }
389
+ }
390
+
391
+ function delay(timeout) {
392
+ var __method = this, args = slice.call(arguments, 1);
393
+ timeout = timeout * 1000;
394
+ return window.setTimeout(function() {
395
+ return __method.apply(__method, args);
396
+ }, timeout);
397
+ }
398
+
399
+ function defer() {
400
+ var args = update([0.01], arguments);
401
+ return this.delay.apply(this, args);
402
+ }
403
+
404
+ function wrap(wrapper) {
405
+ var __method = this;
406
+ return function() {
407
+ var a = update([__method.bind(this)], arguments);
408
+ return wrapper.apply(this, a);
409
+ }
410
+ }
411
+
412
+ function methodize() {
413
+ if (this._methodized) return this._methodized;
414
+ var __method = this;
415
+ return this._methodized = function() {
416
+ var a = update([this], arguments);
417
+ return __method.apply(null, a);
418
+ };
419
+ }
420
+
421
+ return {
422
+ argumentNames: argumentNames,
423
+ bind: bind,
424
+ bindAsEventListener: bindAsEventListener,
425
+ curry: curry,
426
+ delay: delay,
427
+ defer: defer,
428
+ wrap: wrap,
429
+ methodize: methodize
430
+ }
431
+ })());
432
+
433
+
434
+
435
+ (function(proto) {
436
+
437
+
438
+ function toISOString() {
439
+ return this.getUTCFullYear() + '-' +
440
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
441
+ this.getUTCDate().toPaddedString(2) + 'T' +
442
+ this.getUTCHours().toPaddedString(2) + ':' +
443
+ this.getUTCMinutes().toPaddedString(2) + ':' +
444
+ this.getUTCSeconds().toPaddedString(2) + 'Z';
445
+ }
446
+
447
+
448
+ function toJSON() {
449
+ return this.toISOString();
450
+ }
451
+
452
+ if (!proto.toISOString) proto.toISOString = toISOString;
453
+ if (!proto.toJSON) proto.toJSON = toJSON;
454
+
455
+ })(Date.prototype);
456
+
457
+
458
+ RegExp.prototype.match = RegExp.prototype.test;
459
+
460
+ RegExp.escape = function(str) {
461
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
462
+ };
463
+ var PeriodicalExecuter = Class.create({
464
+ initialize: function(callback, frequency) {
465
+ this.callback = callback;
466
+ this.frequency = frequency;
467
+ this.currentlyExecuting = false;
468
+
469
+ this.registerCallback();
470
+ },
471
+
472
+ registerCallback: function() {
473
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
474
+ },
475
+
476
+ execute: function() {
477
+ this.callback(this);
478
+ },
479
+
480
+ stop: function() {
481
+ if (!this.timer) return;
482
+ clearInterval(this.timer);
483
+ this.timer = null;
484
+ },
485
+
486
+ onTimerEvent: function() {
487
+ if (!this.currentlyExecuting) {
488
+ try {
489
+ this.currentlyExecuting = true;
490
+ this.execute();
491
+ this.currentlyExecuting = false;
492
+ } catch(e) {
493
+ this.currentlyExecuting = false;
494
+ throw e;
495
+ }
496
+ }
497
+ }
498
+ });
499
+ Object.extend(String, {
500
+ interpret: function(value) {
501
+ return value == null ? '' : String(value);
502
+ },
503
+ specialChar: {
504
+ '\b': '\\b',
505
+ '\t': '\\t',
506
+ '\n': '\\n',
507
+ '\f': '\\f',
508
+ '\r': '\\r',
509
+ '\\': '\\\\'
510
+ }
511
+ });
512
+
513
+ Object.extend(String.prototype, (function() {
514
+ var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
515
+ typeof JSON.parse === 'function' &&
516
+ JSON.parse('{"test": true}').test;
517
+
518
+ function prepareReplacement(replacement) {
519
+ if (Object.isFunction(replacement)) return replacement;
520
+ var template = new Template(replacement);
521
+ return function(match) { return template.evaluate(match) };
522
+ }
523
+
524
+ function gsub(pattern, replacement) {
525
+ var result = '', source = this, match;
526
+ replacement = prepareReplacement(replacement);
527
+
528
+ if (Object.isString(pattern))
529
+ pattern = RegExp.escape(pattern);
530
+
531
+ if (!(pattern.length || pattern.source)) {
532
+ replacement = replacement('');
533
+ return replacement + source.split('').join(replacement) + replacement;
534
+ }
535
+
536
+ while (source.length > 0) {
537
+ if (match = source.match(pattern)) {
538
+ result += source.slice(0, match.index);
539
+ result += String.interpret(replacement(match));
540
+ source = source.slice(match.index + match[0].length);
541
+ } else {
542
+ result += source, source = '';
543
+ }
544
+ }
545
+ return result;
546
+ }
547
+
548
+ function sub(pattern, replacement, count) {
549
+ replacement = prepareReplacement(replacement);
550
+ count = Object.isUndefined(count) ? 1 : count;
551
+
552
+ return this.gsub(pattern, function(match) {
553
+ if (--count < 0) return match[0];
554
+ return replacement(match);
555
+ });
556
+ }
557
+
558
+ function scan(pattern, iterator) {
559
+ this.gsub(pattern, iterator);
560
+ return String(this);
561
+ }
562
+
563
+ function truncate(length, truncation) {
564
+ length = length || 30;
565
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
566
+ return this.length > length ?
567
+ this.slice(0, length - truncation.length) + truncation : String(this);
568
+ }
569
+
570
+ function strip() {
571
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
572
+ }
573
+
574
+ function stripTags() {
575
+ return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
576
+ }
577
+
578
+ function stripScripts() {
579
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
580
+ }
581
+
582
+ function extractScripts() {
583
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
584
+ matchOne = new RegExp(Prototype.ScriptFragment, 'im');
585
+ return (this.match(matchAll) || []).map(function(scriptTag) {
586
+ return (scriptTag.match(matchOne) || ['', ''])[1];
587
+ });
588
+ }
589
+
590
+ function evalScripts() {
591
+ return this.extractScripts().map(function(script) { return eval(script) });
592
+ }
593
+
594
+ function escapeHTML() {
595
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
596
+ }
597
+
598
+ function unescapeHTML() {
599
+ return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
600
+ }
601
+
602
+
603
+ function toQueryParams(separator) {
604
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
605
+ if (!match) return { };
606
+
607
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
608
+ if ((pair = pair.split('='))[0]) {
609
+ var key = decodeURIComponent(pair.shift()),
610
+ value = pair.length > 1 ? pair.join('=') : pair[0];
611
+
612
+ if (value != undefined) value = decodeURIComponent(value);
613
+
614
+ if (key in hash) {
615
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
616
+ hash[key].push(value);
617
+ }
618
+ else hash[key] = value;
619
+ }
620
+ return hash;
621
+ });
622
+ }
623
+
624
+ function toArray() {
625
+ return this.split('');
626
+ }
627
+
628
+ function succ() {
629
+ return this.slice(0, this.length - 1) +
630
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
631
+ }
632
+
633
+ function times(count) {
634
+ return count < 1 ? '' : new Array(count + 1).join(this);
635
+ }
636
+
637
+ function camelize() {
638
+ return this.replace(/-+(.)?/g, function(match, chr) {
639
+ return chr ? chr.toUpperCase() : '';
640
+ });
641
+ }
642
+
643
+ function capitalize() {
644
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
645
+ }
646
+
647
+ function underscore() {
648
+ return this.replace(/::/g, '/')
649
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
650
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
651
+ .replace(/-/g, '_')
652
+ .toLowerCase();
653
+ }
654
+
655
+ function dasherize() {
656
+ return this.replace(/_/g, '-');
657
+ }
658
+
659
+ function inspect(useDoubleQuotes) {
660
+ var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
661
+ if (character in String.specialChar) {
662
+ return String.specialChar[character];
663
+ }
664
+ return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
665
+ });
666
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
667
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
668
+ }
669
+
670
+ function unfilterJSON(filter) {
671
+ return this.replace(filter || Prototype.JSONFilter, '$1');
672
+ }
673
+
674
+ function isJSON() {
675
+ var str = this;
676
+ if (str.blank()) return false;
677
+ str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
678
+ str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
679
+ str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
680
+ return (/^[\],:{}\s]*$/).test(str);
681
+ }
682
+
683
+ function evalJSON(sanitize) {
684
+ var json = this.unfilterJSON(),
685
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
686
+ if (cx.test(json)) {
687
+ json = json.replace(cx, function (a) {
688
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
689
+ });
690
+ }
691
+ try {
692
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
693
+ } catch (e) { }
694
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
695
+ }
696
+
697
+ function parseJSON() {
698
+ var json = this.unfilterJSON();
699
+ return JSON.parse(json);
700
+ }
701
+
702
+ function include(pattern) {
703
+ return this.indexOf(pattern) > -1;
704
+ }
705
+
706
+ function startsWith(pattern) {
707
+ return this.lastIndexOf(pattern, 0) === 0;
708
+ }
709
+
710
+ function endsWith(pattern) {
711
+ var d = this.length - pattern.length;
712
+ return d >= 0 && this.indexOf(pattern, d) === d;
713
+ }
714
+
715
+ function empty() {
716
+ return this == '';
717
+ }
718
+
719
+ function blank() {
720
+ return /^\s*$/.test(this);
721
+ }
722
+
723
+ function interpolate(object, pattern) {
724
+ return new Template(this, pattern).evaluate(object);
725
+ }
726
+
727
+ return {
728
+ gsub: gsub,
729
+ sub: sub,
730
+ scan: scan,
731
+ truncate: truncate,
732
+ strip: String.prototype.trim || strip,
733
+ stripTags: stripTags,
734
+ stripScripts: stripScripts,
735
+ extractScripts: extractScripts,
736
+ evalScripts: evalScripts,
737
+ escapeHTML: escapeHTML,
738
+ unescapeHTML: unescapeHTML,
739
+ toQueryParams: toQueryParams,
740
+ parseQuery: toQueryParams,
741
+ toArray: toArray,
742
+ succ: succ,
743
+ times: times,
744
+ camelize: camelize,
745
+ capitalize: capitalize,
746
+ underscore: underscore,
747
+ dasherize: dasherize,
748
+ inspect: inspect,
749
+ unfilterJSON: unfilterJSON,
750
+ isJSON: isJSON,
751
+ evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
752
+ include: include,
753
+ startsWith: startsWith,
754
+ endsWith: endsWith,
755
+ empty: empty,
756
+ blank: blank,
757
+ interpolate: interpolate
758
+ };
759
+ })());
760
+
761
+ var Template = Class.create({
762
+ initialize: function(template, pattern) {
763
+ this.template = template.toString();
764
+ this.pattern = pattern || Template.Pattern;
765
+ },
766
+
767
+ evaluate: function(object) {
768
+ if (object && Object.isFunction(object.toTemplateReplacements))
769
+ object = object.toTemplateReplacements();
770
+
771
+ return this.template.gsub(this.pattern, function(match) {
772
+ if (object == null) return (match[1] + '');
773
+
774
+ var before = match[1] || '';
775
+ if (before == '\\') return match[2];
776
+
777
+ var ctx = object, expr = match[3],
778
+ pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
779
+
780
+ match = pattern.exec(expr);
781
+ if (match == null) return before;
782
+
783
+ while (match != null) {
784
+ var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
785
+ ctx = ctx[comp];
786
+ if (null == ctx || '' == match[3]) break;
787
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
788
+ match = pattern.exec(expr);
789
+ }
790
+
791
+ return before + String.interpret(ctx);
792
+ });
793
+ }
794
+ });
795
+ Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
796
+
797
+ var $break = { };
798
+
799
+ var Enumerable = (function() {
800
+ function each(iterator, context) {
801
+ var index = 0;
802
+ try {
803
+ this._each(function(value) {
804
+ iterator.call(context, value, index++);
805
+ });
806
+ } catch (e) {
807
+ if (e != $break) throw e;
808
+ }
809
+ return this;
810
+ }
811
+
812
+ function eachSlice(number, iterator, context) {
813
+ var index = -number, slices = [], array = this.toArray();
814
+ if (number < 1) return array;
815
+ while ((index += number) < array.length)
816
+ slices.push(array.slice(index, index+number));
817
+ return slices.collect(iterator, context);
818
+ }
819
+
820
+ function all(iterator, context) {
821
+ iterator = iterator || Prototype.K;
822
+ var result = true;
823
+ this.each(function(value, index) {
824
+ result = result && !!iterator.call(context, value, index);
825
+ if (!result) throw $break;
826
+ });
827
+ return result;
828
+ }
829
+
830
+ function any(iterator, context) {
831
+ iterator = iterator || Prototype.K;
832
+ var result = false;
833
+ this.each(function(value, index) {
834
+ if (result = !!iterator.call(context, value, index))
835
+ throw $break;
836
+ });
837
+ return result;
838
+ }
839
+
840
+ function collect(iterator, context) {
841
+ iterator = iterator || Prototype.K;
842
+ var results = [];
843
+ this.each(function(value, index) {
844
+ results.push(iterator.call(context, value, index));
845
+ });
846
+ return results;
847
+ }
848
+
849
+ function detect(iterator, context) {
850
+ var result;
851
+ this.each(function(value, index) {
852
+ if (iterator.call(context, value, index)) {
853
+ result = value;
854
+ throw $break;
855
+ }
856
+ });
857
+ return result;
858
+ }
859
+
860
+ function findAll(iterator, context) {
861
+ var results = [];
862
+ this.each(function(value, index) {
863
+ if (iterator.call(context, value, index))
864
+ results.push(value);
865
+ });
866
+ return results;
867
+ }
868
+
869
+ function grep(filter, iterator, context) {
870
+ iterator = iterator || Prototype.K;
871
+ var results = [];
872
+
873
+ if (Object.isString(filter))
874
+ filter = new RegExp(RegExp.escape(filter));
875
+
876
+ this.each(function(value, index) {
877
+ if (filter.match(value))
878
+ results.push(iterator.call(context, value, index));
879
+ });
880
+ return results;
881
+ }
882
+
883
+ function include(object) {
884
+ if (Object.isFunction(this.indexOf))
885
+ if (this.indexOf(object) != -1) return true;
886
+
887
+ var found = false;
888
+ this.each(function(value) {
889
+ if (value == object) {
890
+ found = true;
891
+ throw $break;
892
+ }
893
+ });
894
+ return found;
895
+ }
896
+
897
+ function inGroupsOf(number, fillWith) {
898
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
899
+ return this.eachSlice(number, function(slice) {
900
+ while(slice.length < number) slice.push(fillWith);
901
+ return slice;
902
+ });
903
+ }
904
+
905
+ function inject(memo, iterator, context) {
906
+ this.each(function(value, index) {
907
+ memo = iterator.call(context, memo, value, index);
908
+ });
909
+ return memo;
910
+ }
911
+
912
+ function invoke(method) {
913
+ var args = $A(arguments).slice(1);
914
+ return this.map(function(value) {
915
+ return value[method].apply(value, args);
916
+ });
917
+ }
918
+
919
+ function max(iterator, context) {
920
+ iterator = iterator || Prototype.K;
921
+ var result;
922
+ this.each(function(value, index) {
923
+ value = iterator.call(context, value, index);
924
+ if (result == null || value >= result)
925
+ result = value;
926
+ });
927
+ return result;
928
+ }
929
+
930
+ function min(iterator, context) {
931
+ iterator = iterator || Prototype.K;
932
+ var result;
933
+ this.each(function(value, index) {
934
+ value = iterator.call(context, value, index);
935
+ if (result == null || value < result)
936
+ result = value;
937
+ });
938
+ return result;
939
+ }
940
+
941
+ function partition(iterator, context) {
942
+ iterator = iterator || Prototype.K;
943
+ var trues = [], falses = [];
944
+ this.each(function(value, index) {
945
+ (iterator.call(context, value, index) ?
946
+ trues : falses).push(value);
947
+ });
948
+ return [trues, falses];
949
+ }
950
+
951
+ function pluck(property) {
952
+ var results = [];
953
+ this.each(function(value) {
954
+ results.push(value[property]);
955
+ });
956
+ return results;
957
+ }
958
+
959
+ function reject(iterator, context) {
960
+ var results = [];
961
+ this.each(function(value, index) {
962
+ if (!iterator.call(context, value, index))
963
+ results.push(value);
964
+ });
965
+ return results;
966
+ }
967
+
968
+ function sortBy(iterator, context) {
969
+ return this.map(function(value, index) {
970
+ return {
971
+ value: value,
972
+ criteria: iterator.call(context, value, index)
973
+ };
974
+ }).sort(function(left, right) {
975
+ var a = left.criteria, b = right.criteria;
976
+ return a < b ? -1 : a > b ? 1 : 0;
977
+ }).pluck('value');
978
+ }
979
+
980
+ function toArray() {
981
+ return this.map();
982
+ }
983
+
984
+ function zip() {
985
+ var iterator = Prototype.K, args = $A(arguments);
986
+ if (Object.isFunction(args.last()))
987
+ iterator = args.pop();
988
+
989
+ var collections = [this].concat(args).map($A);
990
+ return this.map(function(value, index) {
991
+ return iterator(collections.pluck(index));
992
+ });
993
+ }
994
+
995
+ function size() {
996
+ return this.toArray().length;
997
+ }
998
+
999
+ function inspect() {
1000
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
1001
+ }
1002
+
1003
+
1004
+
1005
+
1006
+
1007
+
1008
+
1009
+
1010
+
1011
+ return {
1012
+ each: each,
1013
+ eachSlice: eachSlice,
1014
+ all: all,
1015
+ every: all,
1016
+ any: any,
1017
+ some: any,
1018
+ collect: collect,
1019
+ map: collect,
1020
+ detect: detect,
1021
+ findAll: findAll,
1022
+ select: findAll,
1023
+ filter: findAll,
1024
+ grep: grep,
1025
+ include: include,
1026
+ member: include,
1027
+ inGroupsOf: inGroupsOf,
1028
+ inject: inject,
1029
+ invoke: invoke,
1030
+ max: max,
1031
+ min: min,
1032
+ partition: partition,
1033
+ pluck: pluck,
1034
+ reject: reject,
1035
+ sortBy: sortBy,
1036
+ toArray: toArray,
1037
+ entries: toArray,
1038
+ zip: zip,
1039
+ size: size,
1040
+ inspect: inspect,
1041
+ find: detect
1042
+ };
1043
+ })();
1044
+
1045
+ function $A(iterable) {
1046
+ if (!iterable) return [];
1047
+ if ('toArray' in Object(iterable)) return iterable.toArray();
1048
+ var length = iterable.length || 0, results = new Array(length);
1049
+ while (length--) results[length] = iterable[length];
1050
+ return results;
1051
+ }
1052
+
1053
+
1054
+ function $w(string) {
1055
+ if (!Object.isString(string)) return [];
1056
+ string = string.strip();
1057
+ return string ? string.split(/\s+/) : [];
1058
+ }
1059
+
1060
+ Array.from = $A;
1061
+
1062
+
1063
+ (function() {
1064
+ var arrayProto = Array.prototype,
1065
+ slice = arrayProto.slice,
1066
+ _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
1067
+
1068
+ function each(iterator, context) {
1069
+ for (var i = 0, length = this.length >>> 0; i < length; i++) {
1070
+ if (i in this) iterator.call(context, this[i], i, this);
1071
+ }
1072
+ }
1073
+ if (!_each) _each = each;
1074
+
1075
+ function clear() {
1076
+ this.length = 0;
1077
+ return this;
1078
+ }
1079
+
1080
+ function first() {
1081
+ return this[0];
1082
+ }
1083
+
1084
+ function last() {
1085
+ return this[this.length - 1];
1086
+ }
1087
+
1088
+ function compact() {
1089
+ return this.select(function(value) {
1090
+ return value != null;
1091
+ });
1092
+ }
1093
+
1094
+ function flatten() {
1095
+ return this.inject([], function(array, value) {
1096
+ if (Object.isArray(value))
1097
+ return array.concat(value.flatten());
1098
+ array.push(value);
1099
+ return array;
1100
+ });
1101
+ }
1102
+
1103
+ function without() {
1104
+ var values = slice.call(arguments, 0);
1105
+ return this.select(function(value) {
1106
+ return !values.include(value);
1107
+ });
1108
+ }
1109
+
1110
+ function reverse(inline) {
1111
+ return (inline === false ? this.toArray() : this)._reverse();
1112
+ }
1113
+
1114
+ function uniq(sorted) {
1115
+ return this.inject([], function(array, value, index) {
1116
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1117
+ array.push(value);
1118
+ return array;
1119
+ });
1120
+ }
1121
+
1122
+ function intersect(array) {
1123
+ return this.uniq().findAll(function(item) {
1124
+ return array.detect(function(value) { return item === value });
1125
+ });
1126
+ }
1127
+
1128
+
1129
+ function clone() {
1130
+ return slice.call(this, 0);
1131
+ }
1132
+
1133
+ function size() {
1134
+ return this.length;
1135
+ }
1136
+
1137
+ function inspect() {
1138
+ return '[' + this.map(Object.inspect).join(', ') + ']';
1139
+ }
1140
+
1141
+ function indexOf(item, i) {
1142
+ i || (i = 0);
1143
+ var length = this.length;
1144
+ if (i < 0) i = length + i;
1145
+ for (; i < length; i++)
1146
+ if (this[i] === item) return i;
1147
+ return -1;
1148
+ }
1149
+
1150
+ function lastIndexOf(item, i) {
1151
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
1152
+ var n = this.slice(0, i).reverse().indexOf(item);
1153
+ return (n < 0) ? n : i - n - 1;
1154
+ }
1155
+
1156
+ function concat() {
1157
+ var array = slice.call(this, 0), item;
1158
+ for (var i = 0, length = arguments.length; i < length; i++) {
1159
+ item = arguments[i];
1160
+ if (Object.isArray(item) && !('callee' in item)) {
1161
+ for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
1162
+ array.push(item[j]);
1163
+ } else {
1164
+ array.push(item);
1165
+ }
1166
+ }
1167
+ return array;
1168
+ }
1169
+
1170
+ Object.extend(arrayProto, Enumerable);
1171
+
1172
+ if (!arrayProto._reverse)
1173
+ arrayProto._reverse = arrayProto.reverse;
1174
+
1175
+ Object.extend(arrayProto, {
1176
+ _each: _each,
1177
+ clear: clear,
1178
+ first: first,
1179
+ last: last,
1180
+ compact: compact,
1181
+ flatten: flatten,
1182
+ without: without,
1183
+ reverse: reverse,
1184
+ uniq: uniq,
1185
+ intersect: intersect,
1186
+ clone: clone,
1187
+ toArray: clone,
1188
+ size: size,
1189
+ inspect: inspect
1190
+ });
1191
+
1192
+ var CONCAT_ARGUMENTS_BUGGY = (function() {
1193
+ return [].concat(arguments)[0][0] !== 1;
1194
+ })(1,2)
1195
+
1196
+ if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1197
+
1198
+ if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1199
+ if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1200
+ })();
1201
+ function $H(object) {
1202
+ return new Hash(object);
1203
+ };
1204
+
1205
+ var Hash = Class.create(Enumerable, (function() {
1206
+ function initialize(object) {
1207
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1208
+ }
1209
+
1210
+
1211
+ function _each(iterator) {
1212
+ for (var key in this._object) {
1213
+ var value = this._object[key], pair = [key, value];
1214
+ pair.key = key;
1215
+ pair.value = value;
1216
+ iterator(pair);
1217
+ }
1218
+ }
1219
+
1220
+ function set(key, value) {
1221
+ return this._object[key] = value;
1222
+ }
1223
+
1224
+ function get(key) {
1225
+ if (this._object[key] !== Object.prototype[key])
1226
+ return this._object[key];
1227
+ }
1228
+
1229
+ function unset(key) {
1230
+ var value = this._object[key];
1231
+ delete this._object[key];
1232
+ return value;
1233
+ }
1234
+
1235
+ function toObject() {
1236
+ return Object.clone(this._object);
1237
+ }
1238
+
1239
+
1240
+
1241
+ function keys() {
1242
+ return this.pluck('key');
1243
+ }
1244
+
1245
+ function values() {
1246
+ return this.pluck('value');
1247
+ }
1248
+
1249
+ function index(value) {
1250
+ var match = this.detect(function(pair) {
1251
+ return pair.value === value;
1252
+ });
1253
+ return match && match.key;
1254
+ }
1255
+
1256
+ function merge(object) {
1257
+ return this.clone().update(object);
1258
+ }
1259
+
1260
+ function update(object) {
1261
+ return new Hash(object).inject(this, function(result, pair) {
1262
+ result.set(pair.key, pair.value);
1263
+ return result;
1264
+ });
1265
+ }
1266
+
1267
+ function toQueryPair(key, value) {
1268
+ if (Object.isUndefined(value)) return key;
1269
+ return key + '=' + encodeURIComponent(String.interpret(value));
1270
+ }
1271
+
1272
+ function toQueryString() {
1273
+ return this.inject([], function(results, pair) {
1274
+ var key = encodeURIComponent(pair.key), values = pair.value;
1275
+
1276
+ if (values && typeof values == 'object') {
1277
+ if (Object.isArray(values)) {
1278
+ var queryValues = [];
1279
+ for (var i = 0, len = values.length, value; i < len; i++) {
1280
+ value = values[i];
1281
+ queryValues.push(toQueryPair(key, value));
1282
+ }
1283
+ return results.concat(queryValues);
1284
+ }
1285
+ } else results.push(toQueryPair(key, values));
1286
+ return results;
1287
+ }).join('&');
1288
+ }
1289
+
1290
+ function inspect() {
1291
+ return '#<Hash:{' + this.map(function(pair) {
1292
+ return pair.map(Object.inspect).join(': ');
1293
+ }).join(', ') + '}>';
1294
+ }
1295
+
1296
+ function clone() {
1297
+ return new Hash(this);
1298
+ }
1299
+
1300
+ return {
1301
+ initialize: initialize,
1302
+ _each: _each,
1303
+ set: set,
1304
+ get: get,
1305
+ unset: unset,
1306
+ toObject: toObject,
1307
+ toTemplateReplacements: toObject,
1308
+ keys: keys,
1309
+ values: values,
1310
+ index: index,
1311
+ merge: merge,
1312
+ update: update,
1313
+ toQueryString: toQueryString,
1314
+ inspect: inspect,
1315
+ toJSON: toObject,
1316
+ clone: clone
1317
+ };
1318
+ })());
1319
+
1320
+ Hash.from = $H;
1321
+ Object.extend(Number.prototype, (function() {
1322
+ function toColorPart() {
1323
+ return this.toPaddedString(2, 16);
1324
+ }
1325
+
1326
+ function succ() {
1327
+ return this + 1;
1328
+ }
1329
+
1330
+ function times(iterator, context) {
1331
+ $R(0, this, true).each(iterator, context);
1332
+ return this;
1333
+ }
1334
+
1335
+ function toPaddedString(length, radix) {
1336
+ var string = this.toString(radix || 10);
1337
+ return '0'.times(length - string.length) + string;
1338
+ }
1339
+
1340
+ function abs() {
1341
+ return Math.abs(this);
1342
+ }
1343
+
1344
+ function round() {
1345
+ return Math.round(this);
1346
+ }
1347
+
1348
+ function ceil() {
1349
+ return Math.ceil(this);
1350
+ }
1351
+
1352
+ function floor() {
1353
+ return Math.floor(this);
1354
+ }
1355
+
1356
+ return {
1357
+ toColorPart: toColorPart,
1358
+ succ: succ,
1359
+ times: times,
1360
+ toPaddedString: toPaddedString,
1361
+ abs: abs,
1362
+ round: round,
1363
+ ceil: ceil,
1364
+ floor: floor
1365
+ };
1366
+ })());
1367
+
1368
+ function $R(start, end, exclusive) {
1369
+ return new ObjectRange(start, end, exclusive);
1370
+ }
1371
+
1372
+ var ObjectRange = Class.create(Enumerable, (function() {
1373
+ function initialize(start, end, exclusive) {
1374
+ this.start = start;
1375
+ this.end = end;
1376
+ this.exclusive = exclusive;
1377
+ }
1378
+
1379
+ function _each(iterator) {
1380
+ var value = this.start;
1381
+ while (this.include(value)) {
1382
+ iterator(value);
1383
+ value = value.succ();
1384
+ }
1385
+ }
1386
+
1387
+ function include(value) {
1388
+ if (value < this.start)
1389
+ return false;
1390
+ if (this.exclusive)
1391
+ return value < this.end;
1392
+ return value <= this.end;
1393
+ }
1394
+
1395
+ return {
1396
+ initialize: initialize,
1397
+ _each: _each,
1398
+ include: include
1399
+ };
1400
+ })());
1401
+
1402
+
1403
+
1404
+ var Abstract = { };
1405
+
1406
+
1407
+ var Try = {
1408
+ these: function() {
1409
+ var returnValue;
1410
+
1411
+ for (var i = 0, length = arguments.length; i < length; i++) {
1412
+ var lambda = arguments[i];
1413
+ try {
1414
+ returnValue = lambda();
1415
+ break;
1416
+ } catch (e) { }
1417
+ }
1418
+
1419
+ return returnValue;
1420
+ }
1421
+ };
1422
+
1423
+ var Ajax = {
1424
+ getTransport: function() {
1425
+ return Try.these(
1426
+ function() {return new XMLHttpRequest()},
1427
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1428
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1429
+ ) || false;
1430
+ },
1431
+
1432
+ activeRequestCount: 0
1433
+ };
1434
+
1435
+ Ajax.Responders = {
1436
+ responders: [],
1437
+
1438
+ _each: function(iterator) {
1439
+ this.responders._each(iterator);
1440
+ },
1441
+
1442
+ register: function(responder) {
1443
+ if (!this.include(responder))
1444
+ this.responders.push(responder);
1445
+ },
1446
+
1447
+ unregister: function(responder) {
1448
+ this.responders = this.responders.without(responder);
1449
+ },
1450
+
1451
+ dispatch: function(callback, request, transport, json) {
1452
+ this.each(function(responder) {
1453
+ if (Object.isFunction(responder[callback])) {
1454
+ try {
1455
+ responder[callback].apply(responder, [request, transport, json]);
1456
+ } catch (e) { }
1457
+ }
1458
+ });
1459
+ }
1460
+ };
1461
+
1462
+ Object.extend(Ajax.Responders, Enumerable);
1463
+
1464
+ Ajax.Responders.register({
1465
+ onCreate: function() { Ajax.activeRequestCount++ },
1466
+ onComplete: function() { Ajax.activeRequestCount-- }
1467
+ });
1468
+ Ajax.Base = Class.create({
1469
+ initialize: function(options) {
1470
+ this.options = {
1471
+ method: 'post',
1472
+ asynchronous: true,
1473
+ contentType: 'application/x-www-form-urlencoded',
1474
+ encoding: 'UTF-8',
1475
+ parameters: '',
1476
+ evalJSON: true,
1477
+ evalJS: true
1478
+ };
1479
+ Object.extend(this.options, options || { });
1480
+
1481
+ this.options.method = this.options.method.toLowerCase();
1482
+
1483
+ if (Object.isHash(this.options.parameters))
1484
+ this.options.parameters = this.options.parameters.toObject();
1485
+ }
1486
+ });
1487
+ Ajax.Request = Class.create(Ajax.Base, {
1488
+ _complete: false,
1489
+
1490
+ initialize: function($super, url, options) {
1491
+ $super(options);
1492
+ this.transport = Ajax.getTransport();
1493
+ this.request(url);
1494
+ },
1495
+
1496
+ request: function(url) {
1497
+ this.url = url;
1498
+ this.method = this.options.method;
1499
+ var params = Object.isString(this.options.parameters) ?
1500
+ this.options.parameters :
1501
+ Object.toQueryString(this.options.parameters);
1502
+
1503
+ if (!['get', 'post'].include(this.method)) {
1504
+ params += (params ? '&' : '') + "_method=" + this.method;
1505
+ this.method = 'post';
1506
+ }
1507
+
1508
+ if (params && this.method === 'get') {
1509
+ this.url += (this.url.include('?') ? '&' : '?') + params;
1510
+ }
1511
+
1512
+ this.parameters = params.toQueryParams();
1513
+
1514
+ try {
1515
+ var response = new Ajax.Response(this);
1516
+ if (this.options.onCreate) this.options.onCreate(response);
1517
+ Ajax.Responders.dispatch('onCreate', this, response);
1518
+
1519
+ this.transport.open(this.method.toUpperCase(), this.url,
1520
+ this.options.asynchronous);
1521
+
1522
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1523
+
1524
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
1525
+ this.setRequestHeaders();
1526
+
1527
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1528
+ this.transport.send(this.body);
1529
+
1530
+ /* Force Firefox to handle ready state 4 for synchronous requests */
1531
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
1532
+ this.onStateChange();
1533
+
1534
+ }
1535
+ catch (e) {
1536
+ this.dispatchException(e);
1537
+ }
1538
+ },
1539
+
1540
+ onStateChange: function() {
1541
+ var readyState = this.transport.readyState;
1542
+ if (readyState > 1 && !((readyState == 4) && this._complete))
1543
+ this.respondToReadyState(this.transport.readyState);
1544
+ },
1545
+
1546
+ setRequestHeaders: function() {
1547
+ var headers = {
1548
+ 'X-Requested-With': 'XMLHttpRequest',
1549
+ 'X-Prototype-Version': Prototype.Version,
1550
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1551
+ };
1552
+
1553
+ if (this.method == 'post') {
1554
+ headers['Content-type'] = this.options.contentType +
1555
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
1556
+
1557
+ /* Force "Connection: close" for older Mozilla browsers to work
1558
+ * around a bug where XMLHttpRequest sends an incorrect
1559
+ * Content-length header. See Mozilla Bugzilla #246651.
1560
+ */
1561
+ if (this.transport.overrideMimeType &&
1562
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1563
+ headers['Connection'] = 'close';
1564
+ }
1565
+
1566
+ if (typeof this.options.requestHeaders == 'object') {
1567
+ var extras = this.options.requestHeaders;
1568
+
1569
+ if (Object.isFunction(extras.push))
1570
+ for (var i = 0, length = extras.length; i < length; i += 2)
1571
+ headers[extras[i]] = extras[i+1];
1572
+ else
1573
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1574
+ }
1575
+
1576
+ for (var name in headers)
1577
+ this.transport.setRequestHeader(name, headers[name]);
1578
+ },
1579
+
1580
+ success: function() {
1581
+ var status = this.getStatus();
1582
+ return !status || (status >= 200 && status < 300) || status == 304;
1583
+ },
1584
+
1585
+ getStatus: function() {
1586
+ try {
1587
+ if (this.transport.status === 1223) return 204;
1588
+ return this.transport.status || 0;
1589
+ } catch (e) { return 0 }
1590
+ },
1591
+
1592
+ respondToReadyState: function(readyState) {
1593
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1594
+
1595
+ if (state == 'Complete') {
1596
+ try {
1597
+ this._complete = true;
1598
+ (this.options['on' + response.status]
1599
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1600
+ || Prototype.emptyFunction)(response, response.headerJSON);
1601
+ } catch (e) {
1602
+ this.dispatchException(e);
1603
+ }
1604
+
1605
+ var contentType = response.getHeader('Content-type');
1606
+ if (this.options.evalJS == 'force'
1607
+ || (this.options.evalJS && this.isSameOrigin() && contentType
1608
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1609
+ this.evalResponse();
1610
+ }
1611
+
1612
+ try {
1613
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1614
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1615
+ } catch (e) {
1616
+ this.dispatchException(e);
1617
+ }
1618
+
1619
+ if (state == 'Complete') {
1620
+ this.transport.onreadystatechange = Prototype.emptyFunction;
1621
+ }
1622
+ },
1623
+
1624
+ isSameOrigin: function() {
1625
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1626
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1627
+ protocol: location.protocol,
1628
+ domain: document.domain,
1629
+ port: location.port ? ':' + location.port : ''
1630
+ }));
1631
+ },
1632
+
1633
+ getHeader: function(name) {
1634
+ try {
1635
+ return this.transport.getResponseHeader(name) || null;
1636
+ } catch (e) { return null; }
1637
+ },
1638
+
1639
+ evalResponse: function() {
1640
+ try {
1641
+ return eval((this.transport.responseText || '').unfilterJSON());
1642
+ } catch (e) {
1643
+ this.dispatchException(e);
1644
+ }
1645
+ },
1646
+
1647
+ dispatchException: function(exception) {
1648
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
1649
+ Ajax.Responders.dispatch('onException', this, exception);
1650
+ }
1651
+ });
1652
+
1653
+ Ajax.Request.Events =
1654
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1655
+
1656
+
1657
+
1658
+
1659
+
1660
+
1661
+
1662
+
1663
+ Ajax.Response = Class.create({
1664
+ initialize: function(request){
1665
+ this.request = request;
1666
+ var transport = this.transport = request.transport,
1667
+ readyState = this.readyState = transport.readyState;
1668
+
1669
+ if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1670
+ this.status = this.getStatus();
1671
+ this.statusText = this.getStatusText();
1672
+ this.responseText = String.interpret(transport.responseText);
1673
+ this.headerJSON = this._getHeaderJSON();
1674
+ }
1675
+
1676
+ if (readyState == 4) {
1677
+ var xml = transport.responseXML;
1678
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
1679
+ this.responseJSON = this._getResponseJSON();
1680
+ }
1681
+ },
1682
+
1683
+ status: 0,
1684
+
1685
+ statusText: '',
1686
+
1687
+ getStatus: Ajax.Request.prototype.getStatus,
1688
+
1689
+ getStatusText: function() {
1690
+ try {
1691
+ return this.transport.statusText || '';
1692
+ } catch (e) { return '' }
1693
+ },
1694
+
1695
+ getHeader: Ajax.Request.prototype.getHeader,
1696
+
1697
+ getAllHeaders: function() {
1698
+ try {
1699
+ return this.getAllResponseHeaders();
1700
+ } catch (e) { return null }
1701
+ },
1702
+
1703
+ getResponseHeader: function(name) {
1704
+ return this.transport.getResponseHeader(name);
1705
+ },
1706
+
1707
+ getAllResponseHeaders: function() {
1708
+ return this.transport.getAllResponseHeaders();
1709
+ },
1710
+
1711
+ _getHeaderJSON: function() {
1712
+ var json = this.getHeader('X-JSON');
1713
+ if (!json) return null;
1714
+ json = decodeURIComponent(escape(json));
1715
+ try {
1716
+ return json.evalJSON(this.request.options.sanitizeJSON ||
1717
+ !this.request.isSameOrigin());
1718
+ } catch (e) {
1719
+ this.request.dispatchException(e);
1720
+ }
1721
+ },
1722
+
1723
+ _getResponseJSON: function() {
1724
+ var options = this.request.options;
1725
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
1726
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
1727
+ this.responseText.blank())
1728
+ return null;
1729
+ try {
1730
+ return this.responseText.evalJSON(options.sanitizeJSON ||
1731
+ !this.request.isSameOrigin());
1732
+ } catch (e) {
1733
+ this.request.dispatchException(e);
1734
+ }
1735
+ }
1736
+ });
1737
+
1738
+ Ajax.Updater = Class.create(Ajax.Request, {
1739
+ initialize: function($super, container, url, options) {
1740
+ this.container = {
1741
+ success: (container.success || container),
1742
+ failure: (container.failure || (container.success ? null : container))
1743
+ };
1744
+
1745
+ options = Object.clone(options);
1746
+ var onComplete = options.onComplete;
1747
+ options.onComplete = (function(response, json) {
1748
+ this.updateContent(response.responseText);
1749
+ if (Object.isFunction(onComplete)) onComplete(response, json);
1750
+ }).bind(this);
1751
+
1752
+ $super(url, options);
1753
+ },
1754
+
1755
+ updateContent: function(responseText) {
1756
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
1757
+ options = this.options;
1758
+
1759
+ if (!options.evalScripts) responseText = responseText.stripScripts();
1760
+
1761
+ if (receiver = $(receiver)) {
1762
+ if (options.insertion) {
1763
+ if (Object.isString(options.insertion)) {
1764
+ var insertion = { }; insertion[options.insertion] = responseText;
1765
+ receiver.insert(insertion);
1766
+ }
1767
+ else options.insertion(receiver, responseText);
1768
+ }
1769
+ else receiver.update(responseText);
1770
+ }
1771
+ }
1772
+ });
1773
+
1774
+ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1775
+ initialize: function($super, container, url, options) {
1776
+ $super(options);
1777
+ this.onComplete = this.options.onComplete;
1778
+
1779
+ this.frequency = (this.options.frequency || 2);
1780
+ this.decay = (this.options.decay || 1);
1781
+
1782
+ this.updater = { };
1783
+ this.container = container;
1784
+ this.url = url;
1785
+
1786
+ this.start();
1787
+ },
1788
+
1789
+ start: function() {
1790
+ this.options.onComplete = this.updateComplete.bind(this);
1791
+ this.onTimerEvent();
1792
+ },
1793
+
1794
+ stop: function() {
1795
+ this.updater.options.onComplete = undefined;
1796
+ clearTimeout(this.timer);
1797
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1798
+ },
1799
+
1800
+ updateComplete: function(response) {
1801
+ if (this.options.decay) {
1802
+ this.decay = (response.responseText == this.lastText ?
1803
+ this.decay * this.options.decay : 1);
1804
+
1805
+ this.lastText = response.responseText;
1806
+ }
1807
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1808
+ },
1809
+
1810
+ onTimerEvent: function() {
1811
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
1812
+ }
1813
+ });
1814
+
1815
+
1816
+ function $(element) {
1817
+ if (arguments.length > 1) {
1818
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1819
+ elements.push($(arguments[i]));
1820
+ return elements;
1821
+ }
1822
+ if (Object.isString(element))
1823
+ element = document.getElementById(element);
1824
+ return Element.extend(element);
1825
+ }
1826
+
1827
+ if (Prototype.BrowserFeatures.XPath) {
1828
+ document._getElementsByXPath = function(expression, parentElement) {
1829
+ var results = [];
1830
+ var query = document.evaluate(expression, $(parentElement) || document,
1831
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1832
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
1833
+ results.push(Element.extend(query.snapshotItem(i)));
1834
+ return results;
1835
+ };
1836
+ }
1837
+
1838
+ /*--------------------------------------------------------------------------*/
1839
+
1840
+ if (!Node) var Node = { };
1841
+
1842
+ if (!Node.ELEMENT_NODE) {
1843
+ Object.extend(Node, {
1844
+ ELEMENT_NODE: 1,
1845
+ ATTRIBUTE_NODE: 2,
1846
+ TEXT_NODE: 3,
1847
+ CDATA_SECTION_NODE: 4,
1848
+ ENTITY_REFERENCE_NODE: 5,
1849
+ ENTITY_NODE: 6,
1850
+ PROCESSING_INSTRUCTION_NODE: 7,
1851
+ COMMENT_NODE: 8,
1852
+ DOCUMENT_NODE: 9,
1853
+ DOCUMENT_TYPE_NODE: 10,
1854
+ DOCUMENT_FRAGMENT_NODE: 11,
1855
+ NOTATION_NODE: 12
1856
+ });
1857
+ }
1858
+
1859
+
1860
+
1861
+ (function(global) {
1862
+ function shouldUseCache(tagName, attributes) {
1863
+ if (tagName === 'select') return false;
1864
+ if ('type' in attributes) return false;
1865
+ return true;
1866
+ }
1867
+
1868
+ var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
1869
+ try {
1870
+ var el = document.createElement('<input name="x">');
1871
+ return el.tagName.toLowerCase() === 'input' && el.name === 'x';
1872
+ }
1873
+ catch(err) {
1874
+ return false;
1875
+ }
1876
+ })();
1877
+
1878
+ var element = global.Element;
1879
+
1880
+ global.Element = function(tagName, attributes) {
1881
+ attributes = attributes || { };
1882
+ tagName = tagName.toLowerCase();
1883
+ var cache = Element.cache;
1884
+
1885
+ if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
1886
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
1887
+ delete attributes.name;
1888
+ return Element.writeAttribute(document.createElement(tagName), attributes);
1889
+ }
1890
+
1891
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1892
+
1893
+ var node = shouldUseCache(tagName, attributes) ?
1894
+ cache[tagName].cloneNode(false) : document.createElement(tagName);
1895
+
1896
+ return Element.writeAttribute(node, attributes);
1897
+ };
1898
+
1899
+ Object.extend(global.Element, element || { });
1900
+ if (element) global.Element.prototype = element.prototype;
1901
+
1902
+ })(this);
1903
+
1904
+ Element.idCounter = 1;
1905
+ Element.cache = { };
1906
+
1907
+ Element._purgeElement = function(element) {
1908
+ var uid = element._prototypeUID;
1909
+ if (uid) {
1910
+ Element.stopObserving(element);
1911
+ element._prototypeUID = void 0;
1912
+ delete Element.Storage[uid];
1913
+ }
1914
+ }
1915
+
1916
+ Element.Methods = {
1917
+ visible: function(element) {
1918
+ return $(element).style.display != 'none';
1919
+ },
1920
+
1921
+ toggle: function(element) {
1922
+ element = $(element);
1923
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
1924
+ return element;
1925
+ },
1926
+
1927
+ hide: function(element) {
1928
+ element = $(element);
1929
+ element.style.display = 'none';
1930
+ return element;
1931
+ },
1932
+
1933
+ show: function(element) {
1934
+ element = $(element);
1935
+ element.style.display = '';
1936
+ return element;
1937
+ },
1938
+
1939
+ remove: function(element) {
1940
+ element = $(element);
1941
+ element.parentNode.removeChild(element);
1942
+ return element;
1943
+ },
1944
+
1945
+ update: (function(){
1946
+
1947
+ var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
1948
+ var el = document.createElement("select"),
1949
+ isBuggy = true;
1950
+ el.innerHTML = "<option value=\"test\">test</option>";
1951
+ if (el.options && el.options[0]) {
1952
+ isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
1953
+ }
1954
+ el = null;
1955
+ return isBuggy;
1956
+ })();
1957
+
1958
+ var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
1959
+ try {
1960
+ var el = document.createElement("table");
1961
+ if (el && el.tBodies) {
1962
+ el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
1963
+ var isBuggy = typeof el.tBodies[0] == "undefined";
1964
+ el = null;
1965
+ return isBuggy;
1966
+ }
1967
+ } catch (e) {
1968
+ return true;
1969
+ }
1970
+ })();
1971
+
1972
+ var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
1973
+ try {
1974
+ var el = document.createElement('div');
1975
+ el.innerHTML = "<link>";
1976
+ var isBuggy = (el.childNodes.length === 0);
1977
+ el = null;
1978
+ return isBuggy;
1979
+ } catch(e) {
1980
+ return true;
1981
+ }
1982
+ })();
1983
+
1984
+ var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
1985
+ TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
1986
+
1987
+ var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
1988
+ var s = document.createElement("script"),
1989
+ isBuggy = false;
1990
+ try {
1991
+ s.appendChild(document.createTextNode(""));
1992
+ isBuggy = !s.firstChild ||
1993
+ s.firstChild && s.firstChild.nodeType !== 3;
1994
+ } catch (e) {
1995
+ isBuggy = true;
1996
+ }
1997
+ s = null;
1998
+ return isBuggy;
1999
+ })();
2000
+
2001
+
2002
+ function update(element, content) {
2003
+ element = $(element);
2004
+ var purgeElement = Element._purgeElement;
2005
+
2006
+ var descendants = element.getElementsByTagName('*'),
2007
+ i = descendants.length;
2008
+ while (i--) purgeElement(descendants[i]);
2009
+
2010
+ if (content && content.toElement)
2011
+ content = content.toElement();
2012
+
2013
+ if (Object.isElement(content))
2014
+ return element.update().insert(content);
2015
+
2016
+ content = Object.toHTML(content);
2017
+
2018
+ var tagName = element.tagName.toUpperCase();
2019
+
2020
+ if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
2021
+ element.text = content;
2022
+ return element;
2023
+ }
2024
+
2025
+ if (ANY_INNERHTML_BUGGY) {
2026
+ if (tagName in Element._insertionTranslations.tags) {
2027
+ while (element.firstChild) {
2028
+ element.removeChild(element.firstChild);
2029
+ }
2030
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2031
+ .each(function(node) {
2032
+ element.appendChild(node)
2033
+ });
2034
+ } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
2035
+ while (element.firstChild) {
2036
+ element.removeChild(element.firstChild);
2037
+ }
2038
+ var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
2039
+ nodes.each(function(node) { element.appendChild(node) });
2040
+ }
2041
+ else {
2042
+ element.innerHTML = content.stripScripts();
2043
+ }
2044
+ }
2045
+ else {
2046
+ element.innerHTML = content.stripScripts();
2047
+ }
2048
+
2049
+ content.evalScripts.bind(content).defer();
2050
+ return element;
2051
+ }
2052
+
2053
+ return update;
2054
+ })(),
2055
+
2056
+ replace: function(element, content) {
2057
+ element = $(element);
2058
+ if (content && content.toElement) content = content.toElement();
2059
+ else if (!Object.isElement(content)) {
2060
+ content = Object.toHTML(content);
2061
+ var range = element.ownerDocument.createRange();
2062
+ range.selectNode(element);
2063
+ content.evalScripts.bind(content).defer();
2064
+ content = range.createContextualFragment(content.stripScripts());
2065
+ }
2066
+ element.parentNode.replaceChild(content, element);
2067
+ return element;
2068
+ },
2069
+
2070
+ insert: function(element, insertions) {
2071
+ element = $(element);
2072
+
2073
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
2074
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2075
+ insertions = {bottom:insertions};
2076
+
2077
+ var content, insert, tagName, childNodes;
2078
+
2079
+ for (var position in insertions) {
2080
+ content = insertions[position];
2081
+ position = position.toLowerCase();
2082
+ insert = Element._insertionTranslations[position];
2083
+
2084
+ if (content && content.toElement) content = content.toElement();
2085
+ if (Object.isElement(content)) {
2086
+ insert(element, content);
2087
+ continue;
2088
+ }
2089
+
2090
+ content = Object.toHTML(content);
2091
+
2092
+ tagName = ((position == 'before' || position == 'after')
2093
+ ? element.parentNode : element).tagName.toUpperCase();
2094
+
2095
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2096
+
2097
+ if (position == 'top' || position == 'after') childNodes.reverse();
2098
+ childNodes.each(insert.curry(element));
2099
+
2100
+ content.evalScripts.bind(content).defer();
2101
+ }
2102
+
2103
+ return element;
2104
+ },
2105
+
2106
+ wrap: function(element, wrapper, attributes) {
2107
+ element = $(element);
2108
+ if (Object.isElement(wrapper))
2109
+ $(wrapper).writeAttribute(attributes || { });
2110
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
2111
+ else wrapper = new Element('div', wrapper);
2112
+ if (element.parentNode)
2113
+ element.parentNode.replaceChild(wrapper, element);
2114
+ wrapper.appendChild(element);
2115
+ return wrapper;
2116
+ },
2117
+
2118
+ inspect: function(element) {
2119
+ element = $(element);
2120
+ var result = '<' + element.tagName.toLowerCase();
2121
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
2122
+ var property = pair.first(),
2123
+ attribute = pair.last(),
2124
+ value = (element[property] || '').toString();
2125
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
2126
+ });
2127
+ return result + '>';
2128
+ },
2129
+
2130
+ recursivelyCollect: function(element, property, maximumLength) {
2131
+ element = $(element);
2132
+ maximumLength = maximumLength || -1;
2133
+ var elements = [];
2134
+
2135
+ while (element = element[property]) {
2136
+ if (element.nodeType == 1)
2137
+ elements.push(Element.extend(element));
2138
+ if (elements.length == maximumLength)
2139
+ break;
2140
+ }
2141
+
2142
+ return elements;
2143
+ },
2144
+
2145
+ ancestors: function(element) {
2146
+ return Element.recursivelyCollect(element, 'parentNode');
2147
+ },
2148
+
2149
+ descendants: function(element) {
2150
+ return Element.select(element, "*");
2151
+ },
2152
+
2153
+ firstDescendant: function(element) {
2154
+ element = $(element).firstChild;
2155
+ while (element && element.nodeType != 1) element = element.nextSibling;
2156
+ return $(element);
2157
+ },
2158
+
2159
+ immediateDescendants: function(element) {
2160
+ var results = [], child = $(element).firstChild;
2161
+ while (child) {
2162
+ if (child.nodeType === 1) {
2163
+ results.push(Element.extend(child));
2164
+ }
2165
+ child = child.nextSibling;
2166
+ }
2167
+ return results;
2168
+ },
2169
+
2170
+ previousSiblings: function(element, maximumLength) {
2171
+ return Element.recursivelyCollect(element, 'previousSibling');
2172
+ },
2173
+
2174
+ nextSiblings: function(element) {
2175
+ return Element.recursivelyCollect(element, 'nextSibling');
2176
+ },
2177
+
2178
+ siblings: function(element) {
2179
+ element = $(element);
2180
+ return Element.previousSiblings(element).reverse()
2181
+ .concat(Element.nextSiblings(element));
2182
+ },
2183
+
2184
+ match: function(element, selector) {
2185
+ element = $(element);
2186
+ if (Object.isString(selector))
2187
+ return Prototype.Selector.match(element, selector);
2188
+ return selector.match(element);
2189
+ },
2190
+
2191
+ up: function(element, expression, index) {
2192
+ element = $(element);
2193
+ if (arguments.length == 1) return $(element.parentNode);
2194
+ var ancestors = Element.ancestors(element);
2195
+ return Object.isNumber(expression) ? ancestors[expression] :
2196
+ Prototype.Selector.find(ancestors, expression, index);
2197
+ },
2198
+
2199
+ down: function(element, expression, index) {
2200
+ element = $(element);
2201
+ if (arguments.length == 1) return Element.firstDescendant(element);
2202
+ return Object.isNumber(expression) ? Element.descendants(element)[expression] :
2203
+ Element.select(element, expression)[index || 0];
2204
+ },
2205
+
2206
+ previous: function(element, expression, index) {
2207
+ element = $(element);
2208
+ if (Object.isNumber(expression)) index = expression, expression = false;
2209
+ if (!Object.isNumber(index)) index = 0;
2210
+
2211
+ if (expression) {
2212
+ return Prototype.Selector.find(element.previousSiblings(), expression, index);
2213
+ } else {
2214
+ return element.recursivelyCollect("previousSibling", index + 1)[index];
2215
+ }
2216
+ },
2217
+
2218
+ next: function(element, expression, index) {
2219
+ element = $(element);
2220
+ if (Object.isNumber(expression)) index = expression, expression = false;
2221
+ if (!Object.isNumber(index)) index = 0;
2222
+
2223
+ if (expression) {
2224
+ return Prototype.Selector.find(element.nextSiblings(), expression, index);
2225
+ } else {
2226
+ var maximumLength = Object.isNumber(index) ? index + 1 : 1;
2227
+ return element.recursivelyCollect("nextSibling", index + 1)[index];
2228
+ }
2229
+ },
2230
+
2231
+
2232
+ select: function(element) {
2233
+ element = $(element);
2234
+ var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2235
+ return Prototype.Selector.select(expressions, element);
2236
+ },
2237
+
2238
+ adjacent: function(element) {
2239
+ element = $(element);
2240
+ var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2241
+ return Prototype.Selector.select(expressions, element.parentNode).without(element);
2242
+ },
2243
+
2244
+ identify: function(element) {
2245
+ element = $(element);
2246
+ var id = Element.readAttribute(element, 'id');
2247
+ if (id) return id;
2248
+ do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
2249
+ Element.writeAttribute(element, 'id', id);
2250
+ return id;
2251
+ },
2252
+
2253
+ readAttribute: function(element, name) {
2254
+ element = $(element);
2255
+ if (Prototype.Browser.IE) {
2256
+ var t = Element._attributeTranslations.read;
2257
+ if (t.values[name]) return t.values[name](element, name);
2258
+ if (t.names[name]) name = t.names[name];
2259
+ if (name.include(':')) {
2260
+ return (!element.attributes || !element.attributes[name]) ? null :
2261
+ element.attributes[name].value;
2262
+ }
2263
+ }
2264
+ return element.getAttribute(name);
2265
+ },
2266
+
2267
+ writeAttribute: function(element, name, value) {
2268
+ element = $(element);
2269
+ var attributes = { }, t = Element._attributeTranslations.write;
2270
+
2271
+ if (typeof name == 'object') attributes = name;
2272
+ else attributes[name] = Object.isUndefined(value) ? true : value;
2273
+
2274
+ for (var attr in attributes) {
2275
+ name = t.names[attr] || attr;
2276
+ value = attributes[attr];
2277
+ if (t.values[attr]) name = t.values[attr](element, value);
2278
+ if (value === false || value === null)
2279
+ element.removeAttribute(name);
2280
+ else if (value === true)
2281
+ element.setAttribute(name, name);
2282
+ else element.setAttribute(name, value);
2283
+ }
2284
+ return element;
2285
+ },
2286
+
2287
+ getHeight: function(element) {
2288
+ return Element.getDimensions(element).height;
2289
+ },
2290
+
2291
+ getWidth: function(element) {
2292
+ return Element.getDimensions(element).width;
2293
+ },
2294
+
2295
+ classNames: function(element) {
2296
+ return new Element.ClassNames(element);
2297
+ },
2298
+
2299
+ hasClassName: function(element, className) {
2300
+ if (!(element = $(element))) return;
2301
+ var elementClassName = element.className;
2302
+ return (elementClassName.length > 0 && (elementClassName == className ||
2303
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
2304
+ },
2305
+
2306
+ addClassName: function(element, className) {
2307
+ if (!(element = $(element))) return;
2308
+ if (!Element.hasClassName(element, className))
2309
+ element.className += (element.className ? ' ' : '') + className;
2310
+ return element;
2311
+ },
2312
+
2313
+ removeClassName: function(element, className) {
2314
+ if (!(element = $(element))) return;
2315
+ element.className = element.className.replace(
2316
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
2317
+ return element;
2318
+ },
2319
+
2320
+ toggleClassName: function(element, className) {
2321
+ if (!(element = $(element))) return;
2322
+ return Element[Element.hasClassName(element, className) ?
2323
+ 'removeClassName' : 'addClassName'](element, className);
2324
+ },
2325
+
2326
+ cleanWhitespace: function(element) {
2327
+ element = $(element);
2328
+ var node = element.firstChild;
2329
+ while (node) {
2330
+ var nextNode = node.nextSibling;
2331
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
2332
+ element.removeChild(node);
2333
+ node = nextNode;
2334
+ }
2335
+ return element;
2336
+ },
2337
+
2338
+ empty: function(element) {
2339
+ return $(element).innerHTML.blank();
2340
+ },
2341
+
2342
+ descendantOf: function(element, ancestor) {
2343
+ element = $(element), ancestor = $(ancestor);
2344
+
2345
+ if (element.compareDocumentPosition)
2346
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
2347
+
2348
+ if (ancestor.contains)
2349
+ return ancestor.contains(element) && ancestor !== element;
2350
+
2351
+ while (element = element.parentNode)
2352
+ if (element == ancestor) return true;
2353
+
2354
+ return false;
2355
+ },
2356
+
2357
+ scrollTo: function(element) {
2358
+ element = $(element);
2359
+ var pos = Element.cumulativeOffset(element);
2360
+ window.scrollTo(pos[0], pos[1]);
2361
+ return element;
2362
+ },
2363
+
2364
+ getStyle: function(element, style) {
2365
+ element = $(element);
2366
+ style = style == 'float' ? 'cssFloat' : style.camelize();
2367
+ var value = element.style[style];
2368
+ if (!value || value == 'auto') {
2369
+ var css = document.defaultView.getComputedStyle(element, null);
2370
+ value = css ? css[style] : null;
2371
+ }
2372
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
2373
+ return value == 'auto' ? null : value;
2374
+ },
2375
+
2376
+ getOpacity: function(element) {
2377
+ return $(element).getStyle('opacity');
2378
+ },
2379
+
2380
+ setStyle: function(element, styles) {
2381
+ element = $(element);
2382
+ var elementStyle = element.style, match;
2383
+ if (Object.isString(styles)) {
2384
+ element.style.cssText += ';' + styles;
2385
+ return styles.include('opacity') ?
2386
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
2387
+ }
2388
+ for (var property in styles)
2389
+ if (property == 'opacity') element.setOpacity(styles[property]);
2390
+ else
2391
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
2392
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
2393
+ property] = styles[property];
2394
+
2395
+ return element;
2396
+ },
2397
+
2398
+ setOpacity: function(element, value) {
2399
+ element = $(element);
2400
+ element.style.opacity = (value == 1 || value === '') ? '' :
2401
+ (value < 0.00001) ? 0 : value;
2402
+ return element;
2403
+ },
2404
+
2405
+ makePositioned: function(element) {
2406
+ element = $(element);
2407
+ var pos = Element.getStyle(element, 'position');
2408
+ if (pos == 'static' || !pos) {
2409
+ element._madePositioned = true;
2410
+ element.style.position = 'relative';
2411
+ if (Prototype.Browser.Opera) {
2412
+ element.style.top = 0;
2413
+ element.style.left = 0;
2414
+ }
2415
+ }
2416
+ return element;
2417
+ },
2418
+
2419
+ undoPositioned: function(element) {
2420
+ element = $(element);
2421
+ if (element._madePositioned) {
2422
+ element._madePositioned = undefined;
2423
+ element.style.position =
2424
+ element.style.top =
2425
+ element.style.left =
2426
+ element.style.bottom =
2427
+ element.style.right = '';
2428
+ }
2429
+ return element;
2430
+ },
2431
+
2432
+ makeClipping: function(element) {
2433
+ element = $(element);
2434
+ if (element._overflow) return element;
2435
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
2436
+ if (element._overflow !== 'hidden')
2437
+ element.style.overflow = 'hidden';
2438
+ return element;
2439
+ },
2440
+
2441
+ undoClipping: function(element) {
2442
+ element = $(element);
2443
+ if (!element._overflow) return element;
2444
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2445
+ element._overflow = null;
2446
+ return element;
2447
+ },
2448
+
2449
+ clonePosition: function(element, source) {
2450
+ var options = Object.extend({
2451
+ setLeft: true,
2452
+ setTop: true,
2453
+ setWidth: true,
2454
+ setHeight: true,
2455
+ offsetTop: 0,
2456
+ offsetLeft: 0
2457
+ }, arguments[2] || { });
2458
+
2459
+ source = $(source);
2460
+ var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
2461
+
2462
+ element = $(element);
2463
+
2464
+ if (Element.getStyle(element, 'position') == 'absolute') {
2465
+ parent = Element.getOffsetParent(element);
2466
+ delta = Element.viewportOffset(parent);
2467
+ }
2468
+
2469
+ if (parent == document.body) {
2470
+ delta[0] -= document.body.offsetLeft;
2471
+ delta[1] -= document.body.offsetTop;
2472
+ }
2473
+
2474
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2475
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2476
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
2477
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2478
+ return element;
2479
+ }
2480
+ };
2481
+
2482
+ Object.extend(Element.Methods, {
2483
+ getElementsBySelector: Element.Methods.select,
2484
+
2485
+ childElements: Element.Methods.immediateDescendants
2486
+ });
2487
+
2488
+ Element._attributeTranslations = {
2489
+ write: {
2490
+ names: {
2491
+ className: 'class',
2492
+ htmlFor: 'for'
2493
+ },
2494
+ values: { }
2495
+ }
2496
+ };
2497
+
2498
+ if (Prototype.Browser.Opera) {
2499
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2500
+ function(proceed, element, style) {
2501
+ switch (style) {
2502
+ case 'height': case 'width':
2503
+ if (!Element.visible(element)) return null;
2504
+
2505
+ var dim = parseInt(proceed(element, style), 10);
2506
+
2507
+ if (dim !== element['offset' + style.capitalize()])
2508
+ return dim + 'px';
2509
+
2510
+ var properties;
2511
+ if (style === 'height') {
2512
+ properties = ['border-top-width', 'padding-top',
2513
+ 'padding-bottom', 'border-bottom-width'];
2514
+ }
2515
+ else {
2516
+ properties = ['border-left-width', 'padding-left',
2517
+ 'padding-right', 'border-right-width'];
2518
+ }
2519
+ return properties.inject(dim, function(memo, property) {
2520
+ var val = proceed(element, property);
2521
+ return val === null ? memo : memo - parseInt(val, 10);
2522
+ }) + 'px';
2523
+ default: return proceed(element, style);
2524
+ }
2525
+ }
2526
+ );
2527
+
2528
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2529
+ function(proceed, element, attribute) {
2530
+ if (attribute === 'title') return element.title;
2531
+ return proceed(element, attribute);
2532
+ }
2533
+ );
2534
+ }
2535
+
2536
+ else if (Prototype.Browser.IE) {
2537
+ Element.Methods.getStyle = function(element, style) {
2538
+ element = $(element);
2539
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2540
+ var value = element.style[style];
2541
+ if (!value && element.currentStyle) value = element.currentStyle[style];
2542
+
2543
+ if (style == 'opacity') {
2544
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2545
+ if (value[1]) return parseFloat(value[1]) / 100;
2546
+ return 1.0;
2547
+ }
2548
+
2549
+ if (value == 'auto') {
2550
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2551
+ return element['offset' + style.capitalize()] + 'px';
2552
+ return null;
2553
+ }
2554
+ return value;
2555
+ };
2556
+
2557
+ Element.Methods.setOpacity = function(element, value) {
2558
+ function stripAlpha(filter){
2559
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
2560
+ }
2561
+ element = $(element);
2562
+ var currentStyle = element.currentStyle;
2563
+ if ((currentStyle && !currentStyle.hasLayout) ||
2564
+ (!currentStyle && element.style.zoom == 'normal'))
2565
+ element.style.zoom = 1;
2566
+
2567
+ var filter = element.getStyle('filter'), style = element.style;
2568
+ if (value == 1 || value === '') {
2569
+ (filter = stripAlpha(filter)) ?
2570
+ style.filter = filter : style.removeAttribute('filter');
2571
+ return element;
2572
+ } else if (value < 0.00001) value = 0;
2573
+ style.filter = stripAlpha(filter) +
2574
+ 'alpha(opacity=' + (value * 100) + ')';
2575
+ return element;
2576
+ };
2577
+
2578
+ Element._attributeTranslations = (function(){
2579
+
2580
+ var classProp = 'className',
2581
+ forProp = 'for',
2582
+ el = document.createElement('div');
2583
+
2584
+ el.setAttribute(classProp, 'x');
2585
+
2586
+ if (el.className !== 'x') {
2587
+ el.setAttribute('class', 'x');
2588
+ if (el.className === 'x') {
2589
+ classProp = 'class';
2590
+ }
2591
+ }
2592
+ el = null;
2593
+
2594
+ el = document.createElement('label');
2595
+ el.setAttribute(forProp, 'x');
2596
+ if (el.htmlFor !== 'x') {
2597
+ el.setAttribute('htmlFor', 'x');
2598
+ if (el.htmlFor === 'x') {
2599
+ forProp = 'htmlFor';
2600
+ }
2601
+ }
2602
+ el = null;
2603
+
2604
+ return {
2605
+ read: {
2606
+ names: {
2607
+ 'class': classProp,
2608
+ 'className': classProp,
2609
+ 'for': forProp,
2610
+ 'htmlFor': forProp
2611
+ },
2612
+ values: {
2613
+ _getAttr: function(element, attribute) {
2614
+ return element.getAttribute(attribute);
2615
+ },
2616
+ _getAttr2: function(element, attribute) {
2617
+ return element.getAttribute(attribute, 2);
2618
+ },
2619
+ _getAttrNode: function(element, attribute) {
2620
+ var node = element.getAttributeNode(attribute);
2621
+ return node ? node.value : "";
2622
+ },
2623
+ _getEv: (function(){
2624
+
2625
+ var el = document.createElement('div'), f;
2626
+ el.onclick = Prototype.emptyFunction;
2627
+ var value = el.getAttribute('onclick');
2628
+
2629
+ if (String(value).indexOf('{') > -1) {
2630
+ f = function(element, attribute) {
2631
+ attribute = element.getAttribute(attribute);
2632
+ if (!attribute) return null;
2633
+ attribute = attribute.toString();
2634
+ attribute = attribute.split('{')[1];
2635
+ attribute = attribute.split('}')[0];
2636
+ return attribute.strip();
2637
+ };
2638
+ }
2639
+ else if (value === '') {
2640
+ f = function(element, attribute) {
2641
+ attribute = element.getAttribute(attribute);
2642
+ if (!attribute) return null;
2643
+ return attribute.strip();
2644
+ };
2645
+ }
2646
+ el = null;
2647
+ return f;
2648
+ })(),
2649
+ _flag: function(element, attribute) {
2650
+ return $(element).hasAttribute(attribute) ? attribute : null;
2651
+ },
2652
+ style: function(element) {
2653
+ return element.style.cssText.toLowerCase();
2654
+ },
2655
+ title: function(element) {
2656
+ return element.title;
2657
+ }
2658
+ }
2659
+ }
2660
+ }
2661
+ })();
2662
+
2663
+ Element._attributeTranslations.write = {
2664
+ names: Object.extend({
2665
+ cellpadding: 'cellPadding',
2666
+ cellspacing: 'cellSpacing'
2667
+ }, Element._attributeTranslations.read.names),
2668
+ values: {
2669
+ checked: function(element, value) {
2670
+ element.checked = !!value;
2671
+ },
2672
+
2673
+ style: function(element, value) {
2674
+ element.style.cssText = value ? value : '';
2675
+ }
2676
+ }
2677
+ };
2678
+
2679
+ Element._attributeTranslations.has = {};
2680
+
2681
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2682
+ 'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
2683
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2684
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2685
+ });
2686
+
2687
+ (function(v) {
2688
+ Object.extend(v, {
2689
+ href: v._getAttr2,
2690
+ src: v._getAttr2,
2691
+ type: v._getAttr,
2692
+ action: v._getAttrNode,
2693
+ disabled: v._flag,
2694
+ checked: v._flag,
2695
+ readonly: v._flag,
2696
+ multiple: v._flag,
2697
+ onload: v._getEv,
2698
+ onunload: v._getEv,
2699
+ onclick: v._getEv,
2700
+ ondblclick: v._getEv,
2701
+ onmousedown: v._getEv,
2702
+ onmouseup: v._getEv,
2703
+ onmouseover: v._getEv,
2704
+ onmousemove: v._getEv,
2705
+ onmouseout: v._getEv,
2706
+ onfocus: v._getEv,
2707
+ onblur: v._getEv,
2708
+ onkeypress: v._getEv,
2709
+ onkeydown: v._getEv,
2710
+ onkeyup: v._getEv,
2711
+ onsubmit: v._getEv,
2712
+ onreset: v._getEv,
2713
+ onselect: v._getEv,
2714
+ onchange: v._getEv
2715
+ });
2716
+ })(Element._attributeTranslations.read.values);
2717
+
2718
+ if (Prototype.BrowserFeatures.ElementExtensions) {
2719
+ (function() {
2720
+ function _descendants(element) {
2721
+ var nodes = element.getElementsByTagName('*'), results = [];
2722
+ for (var i = 0, node; node = nodes[i]; i++)
2723
+ if (node.tagName !== "!") // Filter out comment nodes.
2724
+ results.push(node);
2725
+ return results;
2726
+ }
2727
+
2728
+ Element.Methods.down = function(element, expression, index) {
2729
+ element = $(element);
2730
+ if (arguments.length == 1) return element.firstDescendant();
2731
+ return Object.isNumber(expression) ? _descendants(element)[expression] :
2732
+ Element.select(element, expression)[index || 0];
2733
+ }
2734
+ })();
2735
+ }
2736
+
2737
+ }
2738
+
2739
+ else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2740
+ Element.Methods.setOpacity = function(element, value) {
2741
+ element = $(element);
2742
+ element.style.opacity = (value == 1) ? 0.999999 :
2743
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
2744
+ return element;
2745
+ };
2746
+ }
2747
+
2748
+ else if (Prototype.Browser.WebKit) {
2749
+ Element.Methods.setOpacity = function(element, value) {
2750
+ element = $(element);
2751
+ element.style.opacity = (value == 1 || value === '') ? '' :
2752
+ (value < 0.00001) ? 0 : value;
2753
+
2754
+ if (value == 1)
2755
+ if (element.tagName.toUpperCase() == 'IMG' && element.width) {
2756
+ element.width++; element.width--;
2757
+ } else try {
2758
+ var n = document.createTextNode(' ');
2759
+ element.appendChild(n);
2760
+ element.removeChild(n);
2761
+ } catch (e) { }
2762
+
2763
+ return element;
2764
+ };
2765
+ }
2766
+
2767
+ if ('outerHTML' in document.documentElement) {
2768
+ Element.Methods.replace = function(element, content) {
2769
+ element = $(element);
2770
+
2771
+ if (content && content.toElement) content = content.toElement();
2772
+ if (Object.isElement(content)) {
2773
+ element.parentNode.replaceChild(content, element);
2774
+ return element;
2775
+ }
2776
+
2777
+ content = Object.toHTML(content);
2778
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2779
+
2780
+ if (Element._insertionTranslations.tags[tagName]) {
2781
+ var nextSibling = element.next(),
2782
+ fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2783
+ parent.removeChild(element);
2784
+ if (nextSibling)
2785
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2786
+ else
2787
+ fragments.each(function(node) { parent.appendChild(node) });
2788
+ }
2789
+ else element.outerHTML = content.stripScripts();
2790
+
2791
+ content.evalScripts.bind(content).defer();
2792
+ return element;
2793
+ };
2794
+ }
2795
+
2796
+ Element._returnOffset = function(l, t) {
2797
+ var result = [l, t];
2798
+ result.left = l;
2799
+ result.top = t;
2800
+ return result;
2801
+ };
2802
+
2803
+ Element._getContentFromAnonymousElement = function(tagName, html, force) {
2804
+ var div = new Element('div'),
2805
+ t = Element._insertionTranslations.tags[tagName];
2806
+
2807
+ var workaround = false;
2808
+ if (t) workaround = true;
2809
+ else if (force) {
2810
+ workaround = true;
2811
+ t = ['', '', 0];
2812
+ }
2813
+
2814
+ if (workaround) {
2815
+ div.innerHTML = '&nbsp;' + t[0] + html + t[1];
2816
+ div.removeChild(div.firstChild);
2817
+ for (var i = t[2]; i--; ) {
2818
+ div = div.firstChild;
2819
+ }
2820
+ }
2821
+ else {
2822
+ div.innerHTML = html;
2823
+ }
2824
+ return $A(div.childNodes);
2825
+ };
2826
+
2827
+ Element._insertionTranslations = {
2828
+ before: function(element, node) {
2829
+ element.parentNode.insertBefore(node, element);
2830
+ },
2831
+ top: function(element, node) {
2832
+ element.insertBefore(node, element.firstChild);
2833
+ },
2834
+ bottom: function(element, node) {
2835
+ element.appendChild(node);
2836
+ },
2837
+ after: function(element, node) {
2838
+ element.parentNode.insertBefore(node, element.nextSibling);
2839
+ },
2840
+ tags: {
2841
+ TABLE: ['<table>', '</table>', 1],
2842
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
2843
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2844
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2845
+ SELECT: ['<select>', '</select>', 1]
2846
+ }
2847
+ };
2848
+
2849
+ (function() {
2850
+ var tags = Element._insertionTranslations.tags;
2851
+ Object.extend(tags, {
2852
+ THEAD: tags.TBODY,
2853
+ TFOOT: tags.TBODY,
2854
+ TH: tags.TD
2855
+ });
2856
+ })();
2857
+
2858
+ Element.Methods.Simulated = {
2859
+ hasAttribute: function(element, attribute) {
2860
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
2861
+ var node = $(element).getAttributeNode(attribute);
2862
+ return !!(node && node.specified);
2863
+ }
2864
+ };
2865
+
2866
+ Element.Methods.ByTag = { };
2867
+
2868
+ Object.extend(Element, Element.Methods);
2869
+
2870
+ (function(div) {
2871
+
2872
+ if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
2873
+ window.HTMLElement = { };
2874
+ window.HTMLElement.prototype = div['__proto__'];
2875
+ Prototype.BrowserFeatures.ElementExtensions = true;
2876
+ }
2877
+
2878
+ div = null;
2879
+
2880
+ })(document.createElement('div'));
2881
+
2882
+ Element.extend = (function() {
2883
+
2884
+ function checkDeficiency(tagName) {
2885
+ if (typeof window.Element != 'undefined') {
2886
+ var proto = window.Element.prototype;
2887
+ if (proto) {
2888
+ var id = '_' + (Math.random()+'').slice(2),
2889
+ el = document.createElement(tagName);
2890
+ proto[id] = 'x';
2891
+ var isBuggy = (el[id] !== 'x');
2892
+ delete proto[id];
2893
+ el = null;
2894
+ return isBuggy;
2895
+ }
2896
+ }
2897
+ return false;
2898
+ }
2899
+
2900
+ function extendElementWith(element, methods) {
2901
+ for (var property in methods) {
2902
+ var value = methods[property];
2903
+ if (Object.isFunction(value) && !(property in element))
2904
+ element[property] = value.methodize();
2905
+ }
2906
+ }
2907
+
2908
+ var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
2909
+
2910
+ if (Prototype.BrowserFeatures.SpecificElementExtensions) {
2911
+ if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
2912
+ return function(element) {
2913
+ if (element && typeof element._extendedByPrototype == 'undefined') {
2914
+ var t = element.tagName;
2915
+ if (t && (/^(?:object|applet|embed)$/i.test(t))) {
2916
+ extendElementWith(element, Element.Methods);
2917
+ extendElementWith(element, Element.Methods.Simulated);
2918
+ extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
2919
+ }
2920
+ }
2921
+ return element;
2922
+ }
2923
+ }
2924
+ return Prototype.K;
2925
+ }
2926
+
2927
+ var Methods = { }, ByTag = Element.Methods.ByTag;
2928
+
2929
+ var extend = Object.extend(function(element) {
2930
+ if (!element || typeof element._extendedByPrototype != 'undefined' ||
2931
+ element.nodeType != 1 || element == window) return element;
2932
+
2933
+ var methods = Object.clone(Methods),
2934
+ tagName = element.tagName.toUpperCase();
2935
+
2936
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2937
+
2938
+ extendElementWith(element, methods);
2939
+
2940
+ element._extendedByPrototype = Prototype.emptyFunction;
2941
+ return element;
2942
+
2943
+ }, {
2944
+ refresh: function() {
2945
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
2946
+ Object.extend(Methods, Element.Methods);
2947
+ Object.extend(Methods, Element.Methods.Simulated);
2948
+ }
2949
+ }
2950
+ });
2951
+
2952
+ extend.refresh();
2953
+ return extend;
2954
+ })();
2955
+
2956
+ if (document.documentElement.hasAttribute) {
2957
+ Element.hasAttribute = function(element, attribute) {
2958
+ return element.hasAttribute(attribute);
2959
+ };
2960
+ }
2961
+ else {
2962
+ Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
2963
+ }
2964
+
2965
+ Element.addMethods = function(methods) {
2966
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2967
+
2968
+ if (!methods) {
2969
+ Object.extend(Form, Form.Methods);
2970
+ Object.extend(Form.Element, Form.Element.Methods);
2971
+ Object.extend(Element.Methods.ByTag, {
2972
+ "FORM": Object.clone(Form.Methods),
2973
+ "INPUT": Object.clone(Form.Element.Methods),
2974
+ "SELECT": Object.clone(Form.Element.Methods),
2975
+ "TEXTAREA": Object.clone(Form.Element.Methods),
2976
+ "BUTTON": Object.clone(Form.Element.Methods)
2977
+ });
2978
+ }
2979
+
2980
+ if (arguments.length == 2) {
2981
+ var tagName = methods;
2982
+ methods = arguments[1];
2983
+ }
2984
+
2985
+ if (!tagName) Object.extend(Element.Methods, methods || { });
2986
+ else {
2987
+ if (Object.isArray(tagName)) tagName.each(extend);
2988
+ else extend(tagName);
2989
+ }
2990
+
2991
+ function extend(tagName) {
2992
+ tagName = tagName.toUpperCase();
2993
+ if (!Element.Methods.ByTag[tagName])
2994
+ Element.Methods.ByTag[tagName] = { };
2995
+ Object.extend(Element.Methods.ByTag[tagName], methods);
2996
+ }
2997
+
2998
+ function copy(methods, destination, onlyIfAbsent) {
2999
+ onlyIfAbsent = onlyIfAbsent || false;
3000
+ for (var property in methods) {
3001
+ var value = methods[property];
3002
+ if (!Object.isFunction(value)) continue;
3003
+ if (!onlyIfAbsent || !(property in destination))
3004
+ destination[property] = value.methodize();
3005
+ }
3006
+ }
3007
+
3008
+ function findDOMClass(tagName) {
3009
+ var klass;
3010
+ var trans = {
3011
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3012
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3013
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3014
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3015
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3016
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3017
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3018
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3019
+ "FrameSet", "IFRAME": "IFrame"
3020
+ };
3021
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3022
+ if (window[klass]) return window[klass];
3023
+ klass = 'HTML' + tagName + 'Element';
3024
+ if (window[klass]) return window[klass];
3025
+ klass = 'HTML' + tagName.capitalize() + 'Element';
3026
+ if (window[klass]) return window[klass];
3027
+
3028
+ var element = document.createElement(tagName),
3029
+ proto = element['__proto__'] || element.constructor.prototype;
3030
+
3031
+ element = null;
3032
+ return proto;
3033
+ }
3034
+
3035
+ var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
3036
+ Element.prototype;
3037
+
3038
+ if (F.ElementExtensions) {
3039
+ copy(Element.Methods, elementPrototype);
3040
+ copy(Element.Methods.Simulated, elementPrototype, true);
3041
+ }
3042
+
3043
+ if (F.SpecificElementExtensions) {
3044
+ for (var tag in Element.Methods.ByTag) {
3045
+ var klass = findDOMClass(tag);
3046
+ if (Object.isUndefined(klass)) continue;
3047
+ copy(T[tag], klass.prototype);
3048
+ }
3049
+ }
3050
+
3051
+ Object.extend(Element, Element.Methods);
3052
+ delete Element.ByTag;
3053
+
3054
+ if (Element.extend.refresh) Element.extend.refresh();
3055
+ Element.cache = { };
3056
+ };
3057
+
3058
+
3059
+ document.viewport = {
3060
+
3061
+ getDimensions: function() {
3062
+ return { width: this.getWidth(), height: this.getHeight() };
3063
+ },
3064
+
3065
+ getScrollOffsets: function() {
3066
+ return Element._returnOffset(
3067
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
3068
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
3069
+ }
3070
+ };
3071
+
3072
+ (function(viewport) {
3073
+ var B = Prototype.Browser, doc = document, element, property = {};
3074
+
3075
+ function getRootElement() {
3076
+ if (B.WebKit && !doc.evaluate)
3077
+ return document;
3078
+
3079
+ if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
3080
+ return document.body;
3081
+
3082
+ return document.documentElement;
3083
+ }
3084
+
3085
+ function define(D) {
3086
+ if (!element) element = getRootElement();
3087
+
3088
+ property[D] = 'client' + D;
3089
+
3090
+ viewport['get' + D] = function() { return element[property[D]] };
3091
+ return viewport['get' + D]();
3092
+ }
3093
+
3094
+ viewport.getWidth = define.curry('Width');
3095
+
3096
+ viewport.getHeight = define.curry('Height');
3097
+ })(document.viewport);
3098
+
3099
+
3100
+ Element.Storage = {
3101
+ UID: 1
3102
+ };
3103
+
3104
+ Element.addMethods({
3105
+ getStorage: function(element) {
3106
+ if (!(element = $(element))) return;
3107
+
3108
+ var uid;
3109
+ if (element === window) {
3110
+ uid = 0;
3111
+ } else {
3112
+ if (typeof element._prototypeUID === "undefined")
3113
+ element._prototypeUID = Element.Storage.UID++;
3114
+ uid = element._prototypeUID;
3115
+ }
3116
+
3117
+ if (!Element.Storage[uid])
3118
+ Element.Storage[uid] = $H();
3119
+
3120
+ return Element.Storage[uid];
3121
+ },
3122
+
3123
+ store: function(element, key, value) {
3124
+ if (!(element = $(element))) return;
3125
+
3126
+ if (arguments.length === 2) {
3127
+ Element.getStorage(element).update(key);
3128
+ } else {
3129
+ Element.getStorage(element).set(key, value);
3130
+ }
3131
+
3132
+ return element;
3133
+ },
3134
+
3135
+ retrieve: function(element, key, defaultValue) {
3136
+ if (!(element = $(element))) return;
3137
+ var hash = Element.getStorage(element), value = hash.get(key);
3138
+
3139
+ if (Object.isUndefined(value)) {
3140
+ hash.set(key, defaultValue);
3141
+ value = defaultValue;
3142
+ }
3143
+
3144
+ return value;
3145
+ },
3146
+
3147
+ clone: function(element, deep) {
3148
+ if (!(element = $(element))) return;
3149
+ var clone = element.cloneNode(deep);
3150
+ clone._prototypeUID = void 0;
3151
+ if (deep) {
3152
+ var descendants = Element.select(clone, '*'),
3153
+ i = descendants.length;
3154
+ while (i--) {
3155
+ descendants[i]._prototypeUID = void 0;
3156
+ }
3157
+ }
3158
+ return Element.extend(clone);
3159
+ },
3160
+
3161
+ purge: function(element) {
3162
+ if (!(element = $(element))) return;
3163
+ var purgeElement = Element._purgeElement;
3164
+
3165
+ purgeElement(element);
3166
+
3167
+ var descendants = element.getElementsByTagName('*'),
3168
+ i = descendants.length;
3169
+
3170
+ while (i--) purgeElement(descendants[i]);
3171
+
3172
+ return null;
3173
+ }
3174
+ });
3175
+
3176
+ (function() {
3177
+
3178
+ function toDecimal(pctString) {
3179
+ var match = pctString.match(/^(\d+)%?$/i);
3180
+ if (!match) return null;
3181
+ return (Number(match[1]) / 100);
3182
+ }
3183
+
3184
+ function getPixelValue(value, property, context) {
3185
+ var element = null;
3186
+ if (Object.isElement(value)) {
3187
+ element = value;
3188
+ value = element.getStyle(property);
3189
+ }
3190
+
3191
+ if (value === null) {
3192
+ return null;
3193
+ }
3194
+
3195
+ if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
3196
+ return window.parseFloat(value);
3197
+ }
3198
+
3199
+ var isPercentage = value.include('%'), isViewport = (context === document.viewport);
3200
+
3201
+ if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
3202
+ var style = element.style.left, rStyle = element.runtimeStyle.left;
3203
+ element.runtimeStyle.left = element.currentStyle.left;
3204
+ element.style.left = value || 0;
3205
+ value = element.style.pixelLeft;
3206
+ element.style.left = style;
3207
+ element.runtimeStyle.left = rStyle;
3208
+
3209
+ return value;
3210
+ }
3211
+
3212
+ if (element && isPercentage) {
3213
+ context = context || element.parentNode;
3214
+ var decimal = toDecimal(value);
3215
+ var whole = null;
3216
+ var position = element.getStyle('position');
3217
+
3218
+ var isHorizontal = property.include('left') || property.include('right') ||
3219
+ property.include('width');
3220
+
3221
+ var isVertical = property.include('top') || property.include('bottom') ||
3222
+ property.include('height');
3223
+
3224
+ if (context === document.viewport) {
3225
+ if (isHorizontal) {
3226
+ whole = document.viewport.getWidth();
3227
+ } else if (isVertical) {
3228
+ whole = document.viewport.getHeight();
3229
+ }
3230
+ } else {
3231
+ if (isHorizontal) {
3232
+ whole = $(context).measure('width');
3233
+ } else if (isVertical) {
3234
+ whole = $(context).measure('height');
3235
+ }
3236
+ }
3237
+
3238
+ return (whole === null) ? 0 : whole * decimal;
3239
+ }
3240
+
3241
+ return 0;
3242
+ }
3243
+
3244
+ function toCSSPixels(number) {
3245
+ if (Object.isString(number) && number.endsWith('px')) {
3246
+ return number;
3247
+ }
3248
+ return number + 'px';
3249
+ }
3250
+
3251
+ function isDisplayed(element) {
3252
+ var originalElement = element;
3253
+ while (element && element.parentNode) {
3254
+ var display = element.getStyle('display');
3255
+ if (display === 'none') {
3256
+ return false;
3257
+ }
3258
+ element = $(element.parentNode);
3259
+ }
3260
+ return true;
3261
+ }
3262
+
3263
+ var hasLayout = Prototype.K;
3264
+ if ('currentStyle' in document.documentElement) {
3265
+ hasLayout = function(element) {
3266
+ if (!element.currentStyle.hasLayout) {
3267
+ element.style.zoom = 1;
3268
+ }
3269
+ return element;
3270
+ };
3271
+ }
3272
+
3273
+ function cssNameFor(key) {
3274
+ if (key.include('border')) key = key + '-width';
3275
+ return key.camelize();
3276
+ }
3277
+
3278
+ Element.Layout = Class.create(Hash, {
3279
+ initialize: function($super, element, preCompute) {
3280
+ $super();
3281
+ this.element = $(element);
3282
+
3283
+ Element.Layout.PROPERTIES.each( function(property) {
3284
+ this._set(property, null);
3285
+ }, this);
3286
+
3287
+ if (preCompute) {
3288
+ this._preComputing = true;
3289
+ this._begin();
3290
+ Element.Layout.PROPERTIES.each( this._compute, this );
3291
+ this._end();
3292
+ this._preComputing = false;
3293
+ }
3294
+ },
3295
+
3296
+ _set: function(property, value) {
3297
+ return Hash.prototype.set.call(this, property, value);
3298
+ },
3299
+
3300
+ set: function(property, value) {
3301
+ throw "Properties of Element.Layout are read-only.";
3302
+ },
3303
+
3304
+ get: function($super, property) {
3305
+ var value = $super(property);
3306
+ return value === null ? this._compute(property) : value;
3307
+ },
3308
+
3309
+ _begin: function() {
3310
+ if (this._prepared) return;
3311
+
3312
+ var element = this.element;
3313
+ if (isDisplayed(element)) {
3314
+ this._prepared = true;
3315
+ return;
3316
+ }
3317
+
3318
+ var originalStyles = {
3319
+ position: element.style.position || '',
3320
+ width: element.style.width || '',
3321
+ visibility: element.style.visibility || '',
3322
+ display: element.style.display || ''
3323
+ };
3324
+
3325
+ element.store('prototype_original_styles', originalStyles);
3326
+
3327
+ var position = element.getStyle('position'),
3328
+ width = element.getStyle('width');
3329
+
3330
+ if (width === "0px" || width === null) {
3331
+ element.style.display = 'block';
3332
+ width = element.getStyle('width');
3333
+ }
3334
+
3335
+ var context = (position === 'fixed') ? document.viewport :
3336
+ element.parentNode;
3337
+
3338
+ element.setStyle({
3339
+ position: 'absolute',
3340
+ visibility: 'hidden',
3341
+ display: 'block'
3342
+ });
3343
+
3344
+ var positionedWidth = element.getStyle('width');
3345
+
3346
+ var newWidth;
3347
+ if (width && (positionedWidth === width)) {
3348
+ newWidth = getPixelValue(element, 'width', context);
3349
+ } else if (position === 'absolute' || position === 'fixed') {
3350
+ newWidth = getPixelValue(element, 'width', context);
3351
+ } else {
3352
+ var parent = element.parentNode, pLayout = $(parent).getLayout();
3353
+
3354
+ newWidth = pLayout.get('width') -
3355
+ this.get('margin-left') -
3356
+ this.get('border-left') -
3357
+ this.get('padding-left') -
3358
+ this.get('padding-right') -
3359
+ this.get('border-right') -
3360
+ this.get('margin-right');
3361
+ }
3362
+
3363
+ element.setStyle({ width: newWidth + 'px' });
3364
+
3365
+ this._prepared = true;
3366
+ },
3367
+
3368
+ _end: function() {
3369
+ var element = this.element;
3370
+ var originalStyles = element.retrieve('prototype_original_styles');
3371
+ element.store('prototype_original_styles', null);
3372
+ element.setStyle(originalStyles);
3373
+ this._prepared = false;
3374
+ },
3375
+
3376
+ _compute: function(property) {
3377
+ var COMPUTATIONS = Element.Layout.COMPUTATIONS;
3378
+ if (!(property in COMPUTATIONS)) {
3379
+ throw "Property not found.";
3380
+ }
3381
+
3382
+ return this._set(property, COMPUTATIONS[property].call(this, this.element));
3383
+ },
3384
+
3385
+ toObject: function() {
3386
+ var args = $A(arguments);
3387
+ var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3388
+ args.join(' ').split(' ');
3389
+ var obj = {};
3390
+ keys.each( function(key) {
3391
+ if (!Element.Layout.PROPERTIES.include(key)) return;
3392
+ var value = this.get(key);
3393
+ if (value != null) obj[key] = value;
3394
+ }, this);
3395
+ return obj;
3396
+ },
3397
+
3398
+ toHash: function() {
3399
+ var obj = this.toObject.apply(this, arguments);
3400
+ return new Hash(obj);
3401
+ },
3402
+
3403
+ toCSS: function() {
3404
+ var args = $A(arguments);
3405
+ var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3406
+ args.join(' ').split(' ');
3407
+ var css = {};
3408
+
3409
+ keys.each( function(key) {
3410
+ if (!Element.Layout.PROPERTIES.include(key)) return;
3411
+ if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
3412
+
3413
+ var value = this.get(key);
3414
+ if (value != null) css[cssNameFor(key)] = value + 'px';
3415
+ }, this);
3416
+ return css;
3417
+ },
3418
+
3419
+ inspect: function() {
3420
+ return "#<Element.Layout>";
3421
+ }
3422
+ });
3423
+
3424
+ Object.extend(Element.Layout, {
3425
+ PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
3426
+
3427
+ COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
3428
+
3429
+ COMPUTATIONS: {
3430
+ 'height': function(element) {
3431
+ if (!this._preComputing) this._begin();
3432
+
3433
+ var bHeight = this.get('border-box-height');
3434
+ if (bHeight <= 0) {
3435
+ if (!this._preComputing) this._end();
3436
+ return 0;
3437
+ }
3438
+
3439
+ var bTop = this.get('border-top'),
3440
+ bBottom = this.get('border-bottom');
3441
+
3442
+ var pTop = this.get('padding-top'),
3443
+ pBottom = this.get('padding-bottom');
3444
+
3445
+ if (!this._preComputing) this._end();
3446
+
3447
+ return bHeight - bTop - bBottom - pTop - pBottom;
3448
+ },
3449
+
3450
+ 'width': function(element) {
3451
+ if (!this._preComputing) this._begin();
3452
+
3453
+ var bWidth = this.get('border-box-width');
3454
+ if (bWidth <= 0) {
3455
+ if (!this._preComputing) this._end();
3456
+ return 0;
3457
+ }
3458
+
3459
+ var bLeft = this.get('border-left'),
3460
+ bRight = this.get('border-right');
3461
+
3462
+ var pLeft = this.get('padding-left'),
3463
+ pRight = this.get('padding-right');
3464
+
3465
+ if (!this._preComputing) this._end();
3466
+
3467
+ return bWidth - bLeft - bRight - pLeft - pRight;
3468
+ },
3469
+
3470
+ 'padding-box-height': function(element) {
3471
+ var height = this.get('height'),
3472
+ pTop = this.get('padding-top'),
3473
+ pBottom = this.get('padding-bottom');
3474
+
3475
+ return height + pTop + pBottom;
3476
+ },
3477
+
3478
+ 'padding-box-width': function(element) {
3479
+ var width = this.get('width'),
3480
+ pLeft = this.get('padding-left'),
3481
+ pRight = this.get('padding-right');
3482
+
3483
+ return width + pLeft + pRight;
3484
+ },
3485
+
3486
+ 'border-box-height': function(element) {
3487
+ if (!this._preComputing) this._begin();
3488
+ var height = element.offsetHeight;
3489
+ if (!this._preComputing) this._end();
3490
+ return height;
3491
+ },
3492
+
3493
+ 'border-box-width': function(element) {
3494
+ if (!this._preComputing) this._begin();
3495
+ var width = element.offsetWidth;
3496
+ if (!this._preComputing) this._end();
3497
+ return width;
3498
+ },
3499
+
3500
+ 'margin-box-height': function(element) {
3501
+ var bHeight = this.get('border-box-height'),
3502
+ mTop = this.get('margin-top'),
3503
+ mBottom = this.get('margin-bottom');
3504
+
3505
+ if (bHeight <= 0) return 0;
3506
+
3507
+ return bHeight + mTop + mBottom;
3508
+ },
3509
+
3510
+ 'margin-box-width': function(element) {
3511
+ var bWidth = this.get('border-box-width'),
3512
+ mLeft = this.get('margin-left'),
3513
+ mRight = this.get('margin-right');
3514
+
3515
+ if (bWidth <= 0) return 0;
3516
+
3517
+ return bWidth + mLeft + mRight;
3518
+ },
3519
+
3520
+ 'top': function(element) {
3521
+ var offset = element.positionedOffset();
3522
+ return offset.top;
3523
+ },
3524
+
3525
+ 'bottom': function(element) {
3526
+ var offset = element.positionedOffset(),
3527
+ parent = element.getOffsetParent(),
3528
+ pHeight = parent.measure('height');
3529
+
3530
+ var mHeight = this.get('border-box-height');
3531
+
3532
+ return pHeight - mHeight - offset.top;
3533
+ },
3534
+
3535
+ 'left': function(element) {
3536
+ var offset = element.positionedOffset();
3537
+ return offset.left;
3538
+ },
3539
+
3540
+ 'right': function(element) {
3541
+ var offset = element.positionedOffset(),
3542
+ parent = element.getOffsetParent(),
3543
+ pWidth = parent.measure('width');
3544
+
3545
+ var mWidth = this.get('border-box-width');
3546
+
3547
+ return pWidth - mWidth - offset.left;
3548
+ },
3549
+
3550
+ 'padding-top': function(element) {
3551
+ return getPixelValue(element, 'paddingTop');
3552
+ },
3553
+
3554
+ 'padding-bottom': function(element) {
3555
+ return getPixelValue(element, 'paddingBottom');
3556
+ },
3557
+
3558
+ 'padding-left': function(element) {
3559
+ return getPixelValue(element, 'paddingLeft');
3560
+ },
3561
+
3562
+ 'padding-right': function(element) {
3563
+ return getPixelValue(element, 'paddingRight');
3564
+ },
3565
+
3566
+ 'border-top': function(element) {
3567
+ return getPixelValue(element, 'borderTopWidth');
3568
+ },
3569
+
3570
+ 'border-bottom': function(element) {
3571
+ return getPixelValue(element, 'borderBottomWidth');
3572
+ },
3573
+
3574
+ 'border-left': function(element) {
3575
+ return getPixelValue(element, 'borderLeftWidth');
3576
+ },
3577
+
3578
+ 'border-right': function(element) {
3579
+ return getPixelValue(element, 'borderRightWidth');
3580
+ },
3581
+
3582
+ 'margin-top': function(element) {
3583
+ return getPixelValue(element, 'marginTop');
3584
+ },
3585
+
3586
+ 'margin-bottom': function(element) {
3587
+ return getPixelValue(element, 'marginBottom');
3588
+ },
3589
+
3590
+ 'margin-left': function(element) {
3591
+ return getPixelValue(element, 'marginLeft');
3592
+ },
3593
+
3594
+ 'margin-right': function(element) {
3595
+ return getPixelValue(element, 'marginRight');
3596
+ }
3597
+ }
3598
+ });
3599
+
3600
+ if ('getBoundingClientRect' in document.documentElement) {
3601
+ Object.extend(Element.Layout.COMPUTATIONS, {
3602
+ 'right': function(element) {
3603
+ var parent = hasLayout(element.getOffsetParent());
3604
+ var rect = element.getBoundingClientRect(),
3605
+ pRect = parent.getBoundingClientRect();
3606
+
3607
+ return (pRect.right - rect.right).round();
3608
+ },
3609
+
3610
+ 'bottom': function(element) {
3611
+ var parent = hasLayout(element.getOffsetParent());
3612
+ var rect = element.getBoundingClientRect(),
3613
+ pRect = parent.getBoundingClientRect();
3614
+
3615
+ return (pRect.bottom - rect.bottom).round();
3616
+ }
3617
+ });
3618
+ }
3619
+
3620
+ Element.Offset = Class.create({
3621
+ initialize: function(left, top) {
3622
+ this.left = left.round();
3623
+ this.top = top.round();
3624
+
3625
+ this[0] = this.left;
3626
+ this[1] = this.top;
3627
+ },
3628
+
3629
+ relativeTo: function(offset) {
3630
+ return new Element.Offset(
3631
+ this.left - offset.left,
3632
+ this.top - offset.top
3633
+ );
3634
+ },
3635
+
3636
+ inspect: function() {
3637
+ return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
3638
+ },
3639
+
3640
+ toString: function() {
3641
+ return "[#{left}, #{top}]".interpolate(this);
3642
+ },
3643
+
3644
+ toArray: function() {
3645
+ return [this.left, this.top];
3646
+ }
3647
+ });
3648
+
3649
+ function getLayout(element, preCompute) {
3650
+ return new Element.Layout(element, preCompute);
3651
+ }
3652
+
3653
+ function measure(element, property) {
3654
+ return $(element).getLayout().get(property);
3655
+ }
3656
+
3657
+ function getDimensions(element) {
3658
+ element = $(element);
3659
+ var display = Element.getStyle(element, 'display');
3660
+
3661
+ if (display && display !== 'none') {
3662
+ return { width: element.offsetWidth, height: element.offsetHeight };
3663
+ }
3664
+
3665
+ var style = element.style;
3666
+ var originalStyles = {
3667
+ visibility: style.visibility,
3668
+ position: style.position,
3669
+ display: style.display
3670
+ };
3671
+
3672
+ var newStyles = {
3673
+ visibility: 'hidden',
3674
+ display: 'block'
3675
+ };
3676
+
3677
+ if (originalStyles.position !== 'fixed')
3678
+ newStyles.position = 'absolute';
3679
+
3680
+ Element.setStyle(element, newStyles);
3681
+
3682
+ var dimensions = {
3683
+ width: element.offsetWidth,
3684
+ height: element.offsetHeight
3685
+ };
3686
+
3687
+ Element.setStyle(element, originalStyles);
3688
+
3689
+ return dimensions;
3690
+ }
3691
+
3692
+ function getOffsetParent(element) {
3693
+ element = $(element);
3694
+
3695
+ if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3696
+ return $(document.body);
3697
+
3698
+ var isInline = (Element.getStyle(element, 'display') === 'inline');
3699
+ if (!isInline && element.offsetParent) return $(element.offsetParent);
3700
+
3701
+ while ((element = element.parentNode) && element !== document.body) {
3702
+ if (Element.getStyle(element, 'position') !== 'static') {
3703
+ return isHtml(element) ? $(document.body) : $(element);
3704
+ }
3705
+ }
3706
+
3707
+ return $(document.body);
3708
+ }
3709
+
3710
+
3711
+ function cumulativeOffset(element) {
3712
+ element = $(element);
3713
+ var valueT = 0, valueL = 0;
3714
+ if (element.parentNode) {
3715
+ do {
3716
+ valueT += element.offsetTop || 0;
3717
+ valueL += element.offsetLeft || 0;
3718
+ element = element.offsetParent;
3719
+ } while (element);
3720
+ }
3721
+ return new Element.Offset(valueL, valueT);
3722
+ }
3723
+
3724
+ function positionedOffset(element) {
3725
+ element = $(element);
3726
+
3727
+ var layout = element.getLayout();
3728
+
3729
+ var valueT = 0, valueL = 0;
3730
+ do {
3731
+ valueT += element.offsetTop || 0;
3732
+ valueL += element.offsetLeft || 0;
3733
+ element = element.offsetParent;
3734
+ if (element) {
3735
+ if (isBody(element)) break;
3736
+ var p = Element.getStyle(element, 'position');
3737
+ if (p !== 'static') break;
3738
+ }
3739
+ } while (element);
3740
+
3741
+ valueL -= layout.get('margin-top');
3742
+ valueT -= layout.get('margin-left');
3743
+
3744
+ return new Element.Offset(valueL, valueT);
3745
+ }
3746
+
3747
+ function cumulativeScrollOffset(element) {
3748
+ var valueT = 0, valueL = 0;
3749
+ do {
3750
+ valueT += element.scrollTop || 0;
3751
+ valueL += element.scrollLeft || 0;
3752
+ element = element.parentNode;
3753
+ } while (element);
3754
+ return new Element.Offset(valueL, valueT);
3755
+ }
3756
+
3757
+ function viewportOffset(forElement) {
3758
+ element = $(element);
3759
+ var valueT = 0, valueL = 0, docBody = document.body;
3760
+
3761
+ var element = forElement;
3762
+ do {
3763
+ valueT += element.offsetTop || 0;
3764
+ valueL += element.offsetLeft || 0;
3765
+ if (element.offsetParent == docBody &&
3766
+ Element.getStyle(element, 'position') == 'absolute') break;
3767
+ } while (element = element.offsetParent);
3768
+
3769
+ element = forElement;
3770
+ do {
3771
+ if (element != docBody) {
3772
+ valueT -= element.scrollTop || 0;
3773
+ valueL -= element.scrollLeft || 0;
3774
+ }
3775
+ } while (element = element.parentNode);
3776
+ return new Element.Offset(valueL, valueT);
3777
+ }
3778
+
3779
+ function absolutize(element) {
3780
+ element = $(element);
3781
+
3782
+ if (Element.getStyle(element, 'position') === 'absolute') {
3783
+ return element;
3784
+ }
3785
+
3786
+ var offsetParent = getOffsetParent(element);
3787
+ var eOffset = element.viewportOffset(),
3788
+ pOffset = offsetParent.viewportOffset();
3789
+
3790
+ var offset = eOffset.relativeTo(pOffset);
3791
+ var layout = element.getLayout();
3792
+
3793
+ element.store('prototype_absolutize_original_styles', {
3794
+ left: element.getStyle('left'),
3795
+ top: element.getStyle('top'),
3796
+ width: element.getStyle('width'),
3797
+ height: element.getStyle('height')
3798
+ });
3799
+
3800
+ element.setStyle({
3801
+ position: 'absolute',
3802
+ top: offset.top + 'px',
3803
+ left: offset.left + 'px',
3804
+ width: layout.get('width') + 'px',
3805
+ height: layout.get('height') + 'px'
3806
+ });
3807
+
3808
+ return element;
3809
+ }
3810
+
3811
+ function relativize(element) {
3812
+ element = $(element);
3813
+ if (Element.getStyle(element, 'position') === 'relative') {
3814
+ return element;
3815
+ }
3816
+
3817
+ var originalStyles =
3818
+ element.retrieve('prototype_absolutize_original_styles');
3819
+
3820
+ if (originalStyles) element.setStyle(originalStyles);
3821
+ return element;
3822
+ }
3823
+
3824
+ if (Prototype.Browser.IE) {
3825
+ getOffsetParent = getOffsetParent.wrap(
3826
+ function(proceed, element) {
3827
+ element = $(element);
3828
+
3829
+ if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3830
+ return $(document.body);
3831
+
3832
+ var position = element.getStyle('position');
3833
+ if (position !== 'static') return proceed(element);
3834
+
3835
+ element.setStyle({ position: 'relative' });
3836
+ var value = proceed(element);
3837
+ element.setStyle({ position: position });
3838
+ return value;
3839
+ }
3840
+ );
3841
+
3842
+ positionedOffset = positionedOffset.wrap(function(proceed, element) {
3843
+ element = $(element);
3844
+ if (!element.parentNode) return new Element.Offset(0, 0);
3845
+ var position = element.getStyle('position');
3846
+ if (position !== 'static') return proceed(element);
3847
+
3848
+ var offsetParent = element.getOffsetParent();
3849
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
3850
+ hasLayout(offsetParent);
3851
+
3852
+ element.setStyle({ position: 'relative' });
3853
+ var value = proceed(element);
3854
+ element.setStyle({ position: position });
3855
+ return value;
3856
+ });
3857
+ } else if (Prototype.Browser.Webkit) {
3858
+ cumulativeOffset = function(element) {
3859
+ element = $(element);
3860
+ var valueT = 0, valueL = 0;
3861
+ do {
3862
+ valueT += element.offsetTop || 0;
3863
+ valueL += element.offsetLeft || 0;
3864
+ if (element.offsetParent == document.body)
3865
+ if (Element.getStyle(element, 'position') == 'absolute') break;
3866
+
3867
+ element = element.offsetParent;
3868
+ } while (element);
3869
+
3870
+ return new Element.Offset(valueL, valueT);
3871
+ };
3872
+ }
3873
+
3874
+
3875
+ Element.addMethods({
3876
+ getLayout: getLayout,
3877
+ measure: measure,
3878
+ getDimensions: getDimensions,
3879
+ getOffsetParent: getOffsetParent,
3880
+ cumulativeOffset: cumulativeOffset,
3881
+ positionedOffset: positionedOffset,
3882
+ cumulativeScrollOffset: cumulativeScrollOffset,
3883
+ viewportOffset: viewportOffset,
3884
+ absolutize: absolutize,
3885
+ relativize: relativize
3886
+ });
3887
+
3888
+ function isBody(element) {
3889
+ return element.nodeName.toUpperCase() === 'BODY';
3890
+ }
3891
+
3892
+ function isHtml(element) {
3893
+ return element.nodeName.toUpperCase() === 'HTML';
3894
+ }
3895
+
3896
+ function isDocument(element) {
3897
+ return element.nodeType === Node.DOCUMENT_NODE;
3898
+ }
3899
+
3900
+ function isDetached(element) {
3901
+ return element !== document.body &&
3902
+ !Element.descendantOf(element, document.body);
3903
+ }
3904
+
3905
+ if ('getBoundingClientRect' in document.documentElement) {
3906
+ Element.addMethods({
3907
+ viewportOffset: function(element) {
3908
+ element = $(element);
3909
+ if (isDetached(element)) return new Element.Offset(0, 0);
3910
+
3911
+ var rect = element.getBoundingClientRect(),
3912
+ docEl = document.documentElement;
3913
+ return new Element.Offset(rect.left - docEl.clientLeft,
3914
+ rect.top - docEl.clientTop);
3915
+ }
3916
+ });
3917
+ }
3918
+ })();
3919
+ window.$$ = function() {
3920
+ var expression = $A(arguments).join(', ');
3921
+ return Prototype.Selector.select(expression, document);
3922
+ };
3923
+
3924
+ Prototype.Selector = (function() {
3925
+
3926
+ function select() {
3927
+ throw new Error('Method "Prototype.Selector.select" must be defined.');
3928
+ }
3929
+
3930
+ function match() {
3931
+ throw new Error('Method "Prototype.Selector.match" must be defined.');
3932
+ }
3933
+
3934
+ function find(elements, expression, index) {
3935
+ index = index || 0;
3936
+ var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
3937
+
3938
+ for (i = 0; i < length; i++) {
3939
+ if (match(elements[i], expression) && index == matchIndex++) {
3940
+ return Element.extend(elements[i]);
3941
+ }
3942
+ }
3943
+ }
3944
+
3945
+ function extendElements(elements) {
3946
+ for (var i = 0, length = elements.length; i < length; i++) {
3947
+ Element.extend(elements[i]);
3948
+ }
3949
+ return elements;
3950
+ }
3951
+
3952
+
3953
+ var K = Prototype.K;
3954
+
3955
+ return {
3956
+ select: select,
3957
+ match: match,
3958
+ find: find,
3959
+ extendElements: (Element.extend === K) ? K : extendElements,
3960
+ extendElement: Element.extend
3961
+ };
3962
+ })();
3963
+ /*!
3964
+ * Sizzle CSS Selector Engine - v1.0
3965
+ * Copyright 2009, The Dojo Foundation
3966
+ * Released under the MIT, BSD, and GPL Licenses.
3967
+ * More information: http://sizzlejs.com/
3968
+ */
3969
+ (function(){
3970
+
3971
+ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
3972
+ done = 0,
3973
+ toString = Object.prototype.toString,
3974
+ hasDuplicate = false,
3975
+ baseHasDuplicate = true;
3976
+
3977
+ [0, 0].sort(function(){
3978
+ baseHasDuplicate = false;
3979
+ return 0;
3980
+ });
3981
+
3982
+ var Sizzle = function(selector, context, results, seed) {
3983
+ results = results || [];
3984
+ var origContext = context = context || document;
3985
+
3986
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
3987
+ return [];
3988
+ }
3989
+
3990
+ if ( !selector || typeof selector !== "string" ) {
3991
+ return results;
3992
+ }
3993
+
3994
+ var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
3995
+ soFar = selector;
3996
+
3997
+ while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
3998
+ soFar = m[3];
3999
+
4000
+ parts.push( m[1] );
4001
+
4002
+ if ( m[2] ) {
4003
+ extra = m[3];
4004
+ break;
4005
+ }
4006
+ }
4007
+
4008
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
4009
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
4010
+ set = posProcess( parts[0] + parts[1], context );
4011
+ } else {
4012
+ set = Expr.relative[ parts[0] ] ?
4013
+ [ context ] :
4014
+ Sizzle( parts.shift(), context );
4015
+
4016
+ while ( parts.length ) {
4017
+ selector = parts.shift();
4018
+
4019
+ if ( Expr.relative[ selector ] )
4020
+ selector += parts.shift();
4021
+
4022
+ set = posProcess( selector, set );
4023
+ }
4024
+ }
4025
+ } else {
4026
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
4027
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
4028
+ var ret = Sizzle.find( parts.shift(), context, contextXML );
4029
+ context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
4030
+ }
4031
+
4032
+ if ( context ) {
4033
+ var ret = seed ?
4034
+ { expr: parts.pop(), set: makeArray(seed) } :
4035
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
4036
+ set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
4037
+
4038
+ if ( parts.length > 0 ) {
4039
+ checkSet = makeArray(set);
4040
+ } else {
4041
+ prune = false;
4042
+ }
4043
+
4044
+ while ( parts.length ) {
4045
+ var cur = parts.pop(), pop = cur;
4046
+
4047
+ if ( !Expr.relative[ cur ] ) {
4048
+ cur = "";
4049
+ } else {
4050
+ pop = parts.pop();
4051
+ }
4052
+
4053
+ if ( pop == null ) {
4054
+ pop = context;
4055
+ }
4056
+
4057
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
4058
+ }
4059
+ } else {
4060
+ checkSet = parts = [];
4061
+ }
4062
+ }
4063
+
4064
+ if ( !checkSet ) {
4065
+ checkSet = set;
4066
+ }
4067
+
4068
+ if ( !checkSet ) {
4069
+ throw "Syntax error, unrecognized expression: " + (cur || selector);
4070
+ }
4071
+
4072
+ if ( toString.call(checkSet) === "[object Array]" ) {
4073
+ if ( !prune ) {
4074
+ results.push.apply( results, checkSet );
4075
+ } else if ( context && context.nodeType === 1 ) {
4076
+ for ( var i = 0; checkSet[i] != null; i++ ) {
4077
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
4078
+ results.push( set[i] );
4079
+ }
4080
+ }
4081
+ } else {
4082
+ for ( var i = 0; checkSet[i] != null; i++ ) {
4083
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
4084
+ results.push( set[i] );
4085
+ }
4086
+ }
4087
+ }
4088
+ } else {
4089
+ makeArray( checkSet, results );
4090
+ }
4091
+
4092
+ if ( extra ) {
4093
+ Sizzle( extra, origContext, results, seed );
4094
+ Sizzle.uniqueSort( results );
4095
+ }
4096
+
4097
+ return results;
4098
+ };
4099
+
4100
+ Sizzle.uniqueSort = function(results){
4101
+ if ( sortOrder ) {
4102
+ hasDuplicate = baseHasDuplicate;
4103
+ results.sort(sortOrder);
4104
+
4105
+ if ( hasDuplicate ) {
4106
+ for ( var i = 1; i < results.length; i++ ) {
4107
+ if ( results[i] === results[i-1] ) {
4108
+ results.splice(i--, 1);
4109
+ }
4110
+ }
4111
+ }
4112
+ }
4113
+
4114
+ return results;
4115
+ };
4116
+
4117
+ Sizzle.matches = function(expr, set){
4118
+ return Sizzle(expr, null, null, set);
4119
+ };
4120
+
4121
+ Sizzle.find = function(expr, context, isXML){
4122
+ var set, match;
4123
+
4124
+ if ( !expr ) {
4125
+ return [];
4126
+ }
4127
+
4128
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
4129
+ var type = Expr.order[i], match;
4130
+
4131
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
4132
+ var left = match[1];
4133
+ match.splice(1,1);
4134
+
4135
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
4136
+ match[1] = (match[1] || "").replace(/\\/g, "");
4137
+ set = Expr.find[ type ]( match, context, isXML );
4138
+ if ( set != null ) {
4139
+ expr = expr.replace( Expr.match[ type ], "" );
4140
+ break;
4141
+ }
4142
+ }
4143
+ }
4144
+ }
4145
+
4146
+ if ( !set ) {
4147
+ set = context.getElementsByTagName("*");
4148
+ }
4149
+
4150
+ return {set: set, expr: expr};
4151
+ };
4152
+
4153
+ Sizzle.filter = function(expr, set, inplace, not){
4154
+ var old = expr, result = [], curLoop = set, match, anyFound,
4155
+ isXMLFilter = set && set[0] && isXML(set[0]);
4156
+
4157
+ while ( expr && set.length ) {
4158
+ for ( var type in Expr.filter ) {
4159
+ if ( (match = Expr.match[ type ].exec( expr )) != null ) {
4160
+ var filter = Expr.filter[ type ], found, item;
4161
+ anyFound = false;
4162
+
4163
+ if ( curLoop == result ) {
4164
+ result = [];
4165
+ }
4166
+
4167
+ if ( Expr.preFilter[ type ] ) {
4168
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
4169
+
4170
+ if ( !match ) {
4171
+ anyFound = found = true;
4172
+ } else if ( match === true ) {
4173
+ continue;
4174
+ }
4175
+ }
4176
+
4177
+ if ( match ) {
4178
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
4179
+ if ( item ) {
4180
+ found = filter( item, match, i, curLoop );
4181
+ var pass = not ^ !!found;
4182
+
4183
+ if ( inplace && found != null ) {
4184
+ if ( pass ) {
4185
+ anyFound = true;
4186
+ } else {
4187
+ curLoop[i] = false;
4188
+ }
4189
+ } else if ( pass ) {
4190
+ result.push( item );
4191
+ anyFound = true;
4192
+ }
4193
+ }
4194
+ }
4195
+ }
4196
+
4197
+ if ( found !== undefined ) {
4198
+ if ( !inplace ) {
4199
+ curLoop = result;
4200
+ }
4201
+
4202
+ expr = expr.replace( Expr.match[ type ], "" );
4203
+
4204
+ if ( !anyFound ) {
4205
+ return [];
4206
+ }
4207
+
4208
+ break;
4209
+ }
4210
+ }
4211
+ }
4212
+
4213
+ if ( expr == old ) {
4214
+ if ( anyFound == null ) {
4215
+ throw "Syntax error, unrecognized expression: " + expr;
4216
+ } else {
4217
+ break;
4218
+ }
4219
+ }
4220
+
4221
+ old = expr;
4222
+ }
4223
+
4224
+ return curLoop;
4225
+ };
4226
+
4227
+ var Expr = Sizzle.selectors = {
4228
+ order: [ "ID", "NAME", "TAG" ],
4229
+ match: {
4230
+ ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4231
+ CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4232
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
4233
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
4234
+ TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
4235
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
4236
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
4237
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
4238
+ },
4239
+ leftMatch: {},
4240
+ attrMap: {
4241
+ "class": "className",
4242
+ "for": "htmlFor"
4243
+ },
4244
+ attrHandle: {
4245
+ href: function(elem){
4246
+ return elem.getAttribute("href");
4247
+ }
4248
+ },
4249
+ relative: {
4250
+ "+": function(checkSet, part, isXML){
4251
+ var isPartStr = typeof part === "string",
4252
+ isTag = isPartStr && !/\W/.test(part),
4253
+ isPartStrNotTag = isPartStr && !isTag;
4254
+
4255
+ if ( isTag && !isXML ) {
4256
+ part = part.toUpperCase();
4257
+ }
4258
+
4259
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
4260
+ if ( (elem = checkSet[i]) ) {
4261
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
4262
+
4263
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
4264
+ elem || false :
4265
+ elem === part;
4266
+ }
4267
+ }
4268
+
4269
+ if ( isPartStrNotTag ) {
4270
+ Sizzle.filter( part, checkSet, true );
4271
+ }
4272
+ },
4273
+ ">": function(checkSet, part, isXML){
4274
+ var isPartStr = typeof part === "string";
4275
+
4276
+ if ( isPartStr && !/\W/.test(part) ) {
4277
+ part = isXML ? part : part.toUpperCase();
4278
+
4279
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4280
+ var elem = checkSet[i];
4281
+ if ( elem ) {
4282
+ var parent = elem.parentNode;
4283
+ checkSet[i] = parent.nodeName === part ? parent : false;
4284
+ }
4285
+ }
4286
+ } else {
4287
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4288
+ var elem = checkSet[i];
4289
+ if ( elem ) {
4290
+ checkSet[i] = isPartStr ?
4291
+ elem.parentNode :
4292
+ elem.parentNode === part;
4293
+ }
4294
+ }
4295
+
4296
+ if ( isPartStr ) {
4297
+ Sizzle.filter( part, checkSet, true );
4298
+ }
4299
+ }
4300
+ },
4301
+ "": function(checkSet, part, isXML){
4302
+ var doneName = done++, checkFn = dirCheck;
4303
+
4304
+ if ( !/\W/.test(part) ) {
4305
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
4306
+ checkFn = dirNodeCheck;
4307
+ }
4308
+
4309
+ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
4310
+ },
4311
+ "~": function(checkSet, part, isXML){
4312
+ var doneName = done++, checkFn = dirCheck;
4313
+
4314
+ if ( typeof part === "string" && !/\W/.test(part) ) {
4315
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
4316
+ checkFn = dirNodeCheck;
4317
+ }
4318
+
4319
+ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
4320
+ }
4321
+ },
4322
+ find: {
4323
+ ID: function(match, context, isXML){
4324
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
4325
+ var m = context.getElementById(match[1]);
4326
+ return m ? [m] : [];
4327
+ }
4328
+ },
4329
+ NAME: function(match, context, isXML){
4330
+ if ( typeof context.getElementsByName !== "undefined" ) {
4331
+ var ret = [], results = context.getElementsByName(match[1]);
4332
+
4333
+ for ( var i = 0, l = results.length; i < l; i++ ) {
4334
+ if ( results[i].getAttribute("name") === match[1] ) {
4335
+ ret.push( results[i] );
4336
+ }
4337
+ }
4338
+
4339
+ return ret.length === 0 ? null : ret;
4340
+ }
4341
+ },
4342
+ TAG: function(match, context){
4343
+ return context.getElementsByTagName(match[1]);
4344
+ }
4345
+ },
4346
+ preFilter: {
4347
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
4348
+ match = " " + match[1].replace(/\\/g, "") + " ";
4349
+
4350
+ if ( isXML ) {
4351
+ return match;
4352
+ }
4353
+
4354
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
4355
+ if ( elem ) {
4356
+ if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
4357
+ if ( !inplace )
4358
+ result.push( elem );
4359
+ } else if ( inplace ) {
4360
+ curLoop[i] = false;
4361
+ }
4362
+ }
4363
+ }
4364
+
4365
+ return false;
4366
+ },
4367
+ ID: function(match){
4368
+ return match[1].replace(/\\/g, "");
4369
+ },
4370
+ TAG: function(match, curLoop){
4371
+ for ( var i = 0; curLoop[i] === false; i++ ){}
4372
+ return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
4373
+ },
4374
+ CHILD: function(match){
4375
+ if ( match[1] == "nth" ) {
4376
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
4377
+ match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
4378
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
4379
+
4380
+ match[2] = (test[1] + (test[2] || 1)) - 0;
4381
+ match[3] = test[3] - 0;
4382
+ }
4383
+
4384
+ match[0] = done++;
4385
+
4386
+ return match;
4387
+ },
4388
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
4389
+ var name = match[1].replace(/\\/g, "");
4390
+
4391
+ if ( !isXML && Expr.attrMap[name] ) {
4392
+ match[1] = Expr.attrMap[name];
4393
+ }
4394
+
4395
+ if ( match[2] === "~=" ) {
4396
+ match[4] = " " + match[4] + " ";
4397
+ }
4398
+
4399
+ return match;
4400
+ },
4401
+ PSEUDO: function(match, curLoop, inplace, result, not){
4402
+ if ( match[1] === "not" ) {
4403
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
4404
+ match[3] = Sizzle(match[3], null, null, curLoop);
4405
+ } else {
4406
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
4407
+ if ( !inplace ) {
4408
+ result.push.apply( result, ret );
4409
+ }
4410
+ return false;
4411
+ }
4412
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
4413
+ return true;
4414
+ }
4415
+
4416
+ return match;
4417
+ },
4418
+ POS: function(match){
4419
+ match.unshift( true );
4420
+ return match;
4421
+ }
4422
+ },
4423
+ filters: {
4424
+ enabled: function(elem){
4425
+ return elem.disabled === false && elem.type !== "hidden";
4426
+ },
4427
+ disabled: function(elem){
4428
+ return elem.disabled === true;
4429
+ },
4430
+ checked: function(elem){
4431
+ return elem.checked === true;
4432
+ },
4433
+ selected: function(elem){
4434
+ elem.parentNode.selectedIndex;
4435
+ return elem.selected === true;
4436
+ },
4437
+ parent: function(elem){
4438
+ return !!elem.firstChild;
4439
+ },
4440
+ empty: function(elem){
4441
+ return !elem.firstChild;
4442
+ },
4443
+ has: function(elem, i, match){
4444
+ return !!Sizzle( match[3], elem ).length;
4445
+ },
4446
+ header: function(elem){
4447
+ return /h\d/i.test( elem.nodeName );
4448
+ },
4449
+ text: function(elem){
4450
+ return "text" === elem.type;
4451
+ },
4452
+ radio: function(elem){
4453
+ return "radio" === elem.type;
4454
+ },
4455
+ checkbox: function(elem){
4456
+ return "checkbox" === elem.type;
4457
+ },
4458
+ file: function(elem){
4459
+ return "file" === elem.type;
4460
+ },
4461
+ password: function(elem){
4462
+ return "password" === elem.type;
4463
+ },
4464
+ submit: function(elem){
4465
+ return "submit" === elem.type;
4466
+ },
4467
+ image: function(elem){
4468
+ return "image" === elem.type;
4469
+ },
4470
+ reset: function(elem){
4471
+ return "reset" === elem.type;
4472
+ },
4473
+ button: function(elem){
4474
+ return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
4475
+ },
4476
+ input: function(elem){
4477
+ return /input|select|textarea|button/i.test(elem.nodeName);
4478
+ }
4479
+ },
4480
+ setFilters: {
4481
+ first: function(elem, i){
4482
+ return i === 0;
4483
+ },
4484
+ last: function(elem, i, match, array){
4485
+ return i === array.length - 1;
4486
+ },
4487
+ even: function(elem, i){
4488
+ return i % 2 === 0;
4489
+ },
4490
+ odd: function(elem, i){
4491
+ return i % 2 === 1;
4492
+ },
4493
+ lt: function(elem, i, match){
4494
+ return i < match[3] - 0;
4495
+ },
4496
+ gt: function(elem, i, match){
4497
+ return i > match[3] - 0;
4498
+ },
4499
+ nth: function(elem, i, match){
4500
+ return match[3] - 0 == i;
4501
+ },
4502
+ eq: function(elem, i, match){
4503
+ return match[3] - 0 == i;
4504
+ }
4505
+ },
4506
+ filter: {
4507
+ PSEUDO: function(elem, match, i, array){
4508
+ var name = match[1], filter = Expr.filters[ name ];
4509
+
4510
+ if ( filter ) {
4511
+ return filter( elem, i, match, array );
4512
+ } else if ( name === "contains" ) {
4513
+ return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
4514
+ } else if ( name === "not" ) {
4515
+ var not = match[3];
4516
+
4517
+ for ( var i = 0, l = not.length; i < l; i++ ) {
4518
+ if ( not[i] === elem ) {
4519
+ return false;
4520
+ }
4521
+ }
4522
+
4523
+ return true;
4524
+ }
4525
+ },
4526
+ CHILD: function(elem, match){
4527
+ var type = match[1], node = elem;
4528
+ switch (type) {
4529
+ case 'only':
4530
+ case 'first':
4531
+ while ( (node = node.previousSibling) ) {
4532
+ if ( node.nodeType === 1 ) return false;
4533
+ }
4534
+ if ( type == 'first') return true;
4535
+ node = elem;
4536
+ case 'last':
4537
+ while ( (node = node.nextSibling) ) {
4538
+ if ( node.nodeType === 1 ) return false;
4539
+ }
4540
+ return true;
4541
+ case 'nth':
4542
+ var first = match[2], last = match[3];
4543
+
4544
+ if ( first == 1 && last == 0 ) {
4545
+ return true;
4546
+ }
4547
+
4548
+ var doneName = match[0],
4549
+ parent = elem.parentNode;
4550
+
4551
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
4552
+ var count = 0;
4553
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
4554
+ if ( node.nodeType === 1 ) {
4555
+ node.nodeIndex = ++count;
4556
+ }
4557
+ }
4558
+ parent.sizcache = doneName;
4559
+ }
4560
+
4561
+ var diff = elem.nodeIndex - last;
4562
+ if ( first == 0 ) {
4563
+ return diff == 0;
4564
+ } else {
4565
+ return ( diff % first == 0 && diff / first >= 0 );
4566
+ }
4567
+ }
4568
+ },
4569
+ ID: function(elem, match){
4570
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
4571
+ },
4572
+ TAG: function(elem, match){
4573
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
4574
+ },
4575
+ CLASS: function(elem, match){
4576
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
4577
+ .indexOf( match ) > -1;
4578
+ },
4579
+ ATTR: function(elem, match){
4580
+ var name = match[1],
4581
+ result = Expr.attrHandle[ name ] ?
4582
+ Expr.attrHandle[ name ]( elem ) :
4583
+ elem[ name ] != null ?
4584
+ elem[ name ] :
4585
+ elem.getAttribute( name ),
4586
+ value = result + "",
4587
+ type = match[2],
4588
+ check = match[4];
4589
+
4590
+ return result == null ?
4591
+ type === "!=" :
4592
+ type === "=" ?
4593
+ value === check :
4594
+ type === "*=" ?
4595
+ value.indexOf(check) >= 0 :
4596
+ type === "~=" ?
4597
+ (" " + value + " ").indexOf(check) >= 0 :
4598
+ !check ?
4599
+ value && result !== false :
4600
+ type === "!=" ?
4601
+ value != check :
4602
+ type === "^=" ?
4603
+ value.indexOf(check) === 0 :
4604
+ type === "$=" ?
4605
+ value.substr(value.length - check.length) === check :
4606
+ type === "|=" ?
4607
+ value === check || value.substr(0, check.length + 1) === check + "-" :
4608
+ false;
4609
+ },
4610
+ POS: function(elem, match, i, array){
4611
+ var name = match[2], filter = Expr.setFilters[ name ];
4612
+
4613
+ if ( filter ) {
4614
+ return filter( elem, i, match, array );
4615
+ }
4616
+ }
4617
+ }
4618
+ };
4619
+
4620
+ var origPOS = Expr.match.POS;
4621
+
4622
+ for ( var type in Expr.match ) {
4623
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
4624
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
4625
+ }
4626
+
4627
+ var makeArray = function(array, results) {
4628
+ array = Array.prototype.slice.call( array, 0 );
4629
+
4630
+ if ( results ) {
4631
+ results.push.apply( results, array );
4632
+ return results;
4633
+ }
4634
+
4635
+ return array;
4636
+ };
4637
+
4638
+ try {
4639
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 );
4640
+
4641
+ } catch(e){
4642
+ makeArray = function(array, results) {
4643
+ var ret = results || [];
4644
+
4645
+ if ( toString.call(array) === "[object Array]" ) {
4646
+ Array.prototype.push.apply( ret, array );
4647
+ } else {
4648
+ if ( typeof array.length === "number" ) {
4649
+ for ( var i = 0, l = array.length; i < l; i++ ) {
4650
+ ret.push( array[i] );
4651
+ }
4652
+ } else {
4653
+ for ( var i = 0; array[i]; i++ ) {
4654
+ ret.push( array[i] );
4655
+ }
4656
+ }
4657
+ }
4658
+
4659
+ return ret;
4660
+ };
4661
+ }
4662
+
4663
+ var sortOrder;
4664
+
4665
+ if ( document.documentElement.compareDocumentPosition ) {
4666
+ sortOrder = function( a, b ) {
4667
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
4668
+ if ( a == b ) {
4669
+ hasDuplicate = true;
4670
+ }
4671
+ return 0;
4672
+ }
4673
+
4674
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
4675
+ if ( ret === 0 ) {
4676
+ hasDuplicate = true;
4677
+ }
4678
+ return ret;
4679
+ };
4680
+ } else if ( "sourceIndex" in document.documentElement ) {
4681
+ sortOrder = function( a, b ) {
4682
+ if ( !a.sourceIndex || !b.sourceIndex ) {
4683
+ if ( a == b ) {
4684
+ hasDuplicate = true;
4685
+ }
4686
+ return 0;
4687
+ }
4688
+
4689
+ var ret = a.sourceIndex - b.sourceIndex;
4690
+ if ( ret === 0 ) {
4691
+ hasDuplicate = true;
4692
+ }
4693
+ return ret;
4694
+ };
4695
+ } else if ( document.createRange ) {
4696
+ sortOrder = function( a, b ) {
4697
+ if ( !a.ownerDocument || !b.ownerDocument ) {
4698
+ if ( a == b ) {
4699
+ hasDuplicate = true;
4700
+ }
4701
+ return 0;
4702
+ }
4703
+
4704
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
4705
+ aRange.setStart(a, 0);
4706
+ aRange.setEnd(a, 0);
4707
+ bRange.setStart(b, 0);
4708
+ bRange.setEnd(b, 0);
4709
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
4710
+ if ( ret === 0 ) {
4711
+ hasDuplicate = true;
4712
+ }
4713
+ return ret;
4714
+ };
4715
+ }
4716
+
4717
+ (function(){
4718
+ var form = document.createElement("div"),
4719
+ id = "script" + (new Date).getTime();
4720
+ form.innerHTML = "<a name='" + id + "'/>";
4721
+
4722
+ var root = document.documentElement;
4723
+ root.insertBefore( form, root.firstChild );
4724
+
4725
+ if ( !!document.getElementById( id ) ) {
4726
+ Expr.find.ID = function(match, context, isXML){
4727
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
4728
+ var m = context.getElementById(match[1]);
4729
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
4730
+ }
4731
+ };
4732
+
4733
+ Expr.filter.ID = function(elem, match){
4734
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
4735
+ return elem.nodeType === 1 && node && node.nodeValue === match;
4736
+ };
4737
+ }
4738
+
4739
+ root.removeChild( form );
4740
+ root = form = null; // release memory in IE
4741
+ })();
4742
+
4743
+ (function(){
4744
+
4745
+ var div = document.createElement("div");
4746
+ div.appendChild( document.createComment("") );
4747
+
4748
+ if ( div.getElementsByTagName("*").length > 0 ) {
4749
+ Expr.find.TAG = function(match, context){
4750
+ var results = context.getElementsByTagName(match[1]);
4751
+
4752
+ if ( match[1] === "*" ) {
4753
+ var tmp = [];
4754
+
4755
+ for ( var i = 0; results[i]; i++ ) {
4756
+ if ( results[i].nodeType === 1 ) {
4757
+ tmp.push( results[i] );
4758
+ }
4759
+ }
4760
+
4761
+ results = tmp;
4762
+ }
4763
+
4764
+ return results;
4765
+ };
4766
+ }
4767
+
4768
+ div.innerHTML = "<a href='#'></a>";
4769
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
4770
+ div.firstChild.getAttribute("href") !== "#" ) {
4771
+ Expr.attrHandle.href = function(elem){
4772
+ return elem.getAttribute("href", 2);
4773
+ };
4774
+ }
4775
+
4776
+ div = null; // release memory in IE
4777
+ })();
4778
+
4779
+ if ( document.querySelectorAll ) (function(){
4780
+ var oldSizzle = Sizzle, div = document.createElement("div");
4781
+ div.innerHTML = "<p class='TEST'></p>";
4782
+
4783
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
4784
+ return;
4785
+ }
4786
+
4787
+ Sizzle = function(query, context, extra, seed){
4788
+ context = context || document;
4789
+
4790
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
4791
+ try {
4792
+ return makeArray( context.querySelectorAll(query), extra );
4793
+ } catch(e){}
4794
+ }
4795
+
4796
+ return oldSizzle(query, context, extra, seed);
4797
+ };
4798
+
4799
+ for ( var prop in oldSizzle ) {
4800
+ Sizzle[ prop ] = oldSizzle[ prop ];
4801
+ }
4802
+
4803
+ div = null; // release memory in IE
4804
+ })();
4805
+
4806
+ if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
4807
+ var div = document.createElement("div");
4808
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
4809
+
4810
+ if ( div.getElementsByClassName("e").length === 0 )
4811
+ return;
4812
+
4813
+ div.lastChild.className = "e";
4814
+
4815
+ if ( div.getElementsByClassName("e").length === 1 )
4816
+ return;
4817
+
4818
+ Expr.order.splice(1, 0, "CLASS");
4819
+ Expr.find.CLASS = function(match, context, isXML) {
4820
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
4821
+ return context.getElementsByClassName(match[1]);
4822
+ }
4823
+ };
4824
+
4825
+ div = null; // release memory in IE
4826
+ })();
4827
+
4828
+ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4829
+ var sibDir = dir == "previousSibling" && !isXML;
4830
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4831
+ var elem = checkSet[i];
4832
+ if ( elem ) {
4833
+ if ( sibDir && elem.nodeType === 1 ){
4834
+ elem.sizcache = doneName;
4835
+ elem.sizset = i;
4836
+ }
4837
+ elem = elem[dir];
4838
+ var match = false;
4839
+
4840
+ while ( elem ) {
4841
+ if ( elem.sizcache === doneName ) {
4842
+ match = checkSet[elem.sizset];
4843
+ break;
4844
+ }
4845
+
4846
+ if ( elem.nodeType === 1 && !isXML ){
4847
+ elem.sizcache = doneName;
4848
+ elem.sizset = i;
4849
+ }
4850
+
4851
+ if ( elem.nodeName === cur ) {
4852
+ match = elem;
4853
+ break;
4854
+ }
4855
+
4856
+ elem = elem[dir];
4857
+ }
4858
+
4859
+ checkSet[i] = match;
4860
+ }
4861
+ }
4862
+ }
4863
+
4864
+ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4865
+ var sibDir = dir == "previousSibling" && !isXML;
4866
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4867
+ var elem = checkSet[i];
4868
+ if ( elem ) {
4869
+ if ( sibDir && elem.nodeType === 1 ) {
4870
+ elem.sizcache = doneName;
4871
+ elem.sizset = i;
4872
+ }
4873
+ elem = elem[dir];
4874
+ var match = false;
4875
+
4876
+ while ( elem ) {
4877
+ if ( elem.sizcache === doneName ) {
4878
+ match = checkSet[elem.sizset];
4879
+ break;
4880
+ }
4881
+
4882
+ if ( elem.nodeType === 1 ) {
4883
+ if ( !isXML ) {
4884
+ elem.sizcache = doneName;
4885
+ elem.sizset = i;
4886
+ }
4887
+ if ( typeof cur !== "string" ) {
4888
+ if ( elem === cur ) {
4889
+ match = true;
4890
+ break;
4891
+ }
4892
+
4893
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
4894
+ match = elem;
4895
+ break;
4896
+ }
4897
+ }
4898
+
4899
+ elem = elem[dir];
4900
+ }
4901
+
4902
+ checkSet[i] = match;
4903
+ }
4904
+ }
4905
+ }
4906
+
4907
+ var contains = document.compareDocumentPosition ? function(a, b){
4908
+ return a.compareDocumentPosition(b) & 16;
4909
+ } : function(a, b){
4910
+ return a !== b && (a.contains ? a.contains(b) : true);
4911
+ };
4912
+
4913
+ var isXML = function(elem){
4914
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
4915
+ !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
4916
+ };
4917
+
4918
+ var posProcess = function(selector, context){
4919
+ var tmpSet = [], later = "", match,
4920
+ root = context.nodeType ? [context] : context;
4921
+
4922
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
4923
+ later += match[0];
4924
+ selector = selector.replace( Expr.match.PSEUDO, "" );
4925
+ }
4926
+
4927
+ selector = Expr.relative[selector] ? selector + "*" : selector;
4928
+
4929
+ for ( var i = 0, l = root.length; i < l; i++ ) {
4930
+ Sizzle( selector, root[i], tmpSet );
4931
+ }
4932
+
4933
+ return Sizzle.filter( later, tmpSet );
4934
+ };
4935
+
4936
+
4937
+ window.Sizzle = Sizzle;
4938
+
4939
+ })();
4940
+
4941
+ Prototype._original_property = window.Sizzle;
4942
+
4943
+ ;(function(engine) {
4944
+ var extendElements = Prototype.Selector.extendElements;
4945
+
4946
+ function select(selector, scope) {
4947
+ return extendElements(engine(selector, scope || document));
4948
+ }
4949
+
4950
+ function match(element, selector) {
4951
+ return engine.matches(selector, [element]).length == 1;
4952
+ }
4953
+
4954
+ Prototype.Selector.engine = engine;
4955
+ Prototype.Selector.select = select;
4956
+ Prototype.Selector.match = match;
4957
+ })(Sizzle);
4958
+
4959
+ window.Sizzle = Prototype._original_property;
4960
+ delete Prototype._original_property;
4961
+
4962
+ var Form = {
4963
+ reset: function(form) {
4964
+ form = $(form);
4965
+ form.reset();
4966
+ return form;
4967
+ },
4968
+
4969
+ serializeElements: function(elements, options) {
4970
+ if (typeof options != 'object') options = { hash: !!options };
4971
+ else if (Object.isUndefined(options.hash)) options.hash = true;
4972
+ var key, value, submitted = false, submit = options.submit, accumulator, initial;
4973
+
4974
+ if (options.hash) {
4975
+ initial = {};
4976
+ accumulator = function(result, key, value) {
4977
+ if (key in result) {
4978
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
4979
+ result[key].push(value);
4980
+ } else result[key] = value;
4981
+ return result;
4982
+ };
4983
+ } else {
4984
+ initial = '';
4985
+ accumulator = function(result, key, value) {
4986
+ return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value);
4987
+ }
4988
+ }
4989
+
4990
+ return elements.inject(initial, function(result, element) {
4991
+ if (!element.disabled && element.name) {
4992
+ key = element.name; value = $(element).getValue();
4993
+ if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
4994
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
4995
+ result = accumulator(result, key, value);
4996
+ }
4997
+ }
4998
+ return result;
4999
+ });
5000
+ }
5001
+ };
5002
+
5003
+ Form.Methods = {
5004
+ serialize: function(form, options) {
5005
+ return Form.serializeElements(Form.getElements(form), options);
5006
+ },
5007
+
5008
+ getElements: function(form) {
5009
+ var elements = $(form).getElementsByTagName('*'),
5010
+ element,
5011
+ arr = [ ],
5012
+ serializers = Form.Element.Serializers;
5013
+ for (var i = 0; element = elements[i]; i++) {
5014
+ arr.push(element);
5015
+ }
5016
+ return arr.inject([], function(elements, child) {
5017
+ if (serializers[child.tagName.toLowerCase()])
5018
+ elements.push(Element.extend(child));
5019
+ return elements;
5020
+ })
5021
+ },
5022
+
5023
+ getInputs: function(form, typeName, name) {
5024
+ form = $(form);
5025
+ var inputs = form.getElementsByTagName('input');
5026
+
5027
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
5028
+
5029
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
5030
+ var input = inputs[i];
5031
+ if ((typeName && input.type != typeName) || (name && input.name != name))
5032
+ continue;
5033
+ matchingInputs.push(Element.extend(input));
5034
+ }
5035
+
5036
+ return matchingInputs;
5037
+ },
5038
+
5039
+ disable: function(form) {
5040
+ form = $(form);
5041
+ Form.getElements(form).invoke('disable');
5042
+ return form;
5043
+ },
5044
+
5045
+ enable: function(form) {
5046
+ form = $(form);
5047
+ Form.getElements(form).invoke('enable');
5048
+ return form;
5049
+ },
5050
+
5051
+ findFirstElement: function(form) {
5052
+ var elements = $(form).getElements().findAll(function(element) {
5053
+ return 'hidden' != element.type && !element.disabled;
5054
+ });
5055
+ var firstByIndex = elements.findAll(function(element) {
5056
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
5057
+ }).sortBy(function(element) { return element.tabIndex }).first();
5058
+
5059
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
5060
+ return /^(?:input|select|textarea)$/i.test(element.tagName);
5061
+ });
5062
+ },
5063
+
5064
+ focusFirstElement: function(form) {
5065
+ form = $(form);
5066
+ var element = form.findFirstElement();
5067
+ if (element) element.activate();
5068
+ return form;
5069
+ },
5070
+
5071
+ request: function(form, options) {
5072
+ form = $(form), options = Object.clone(options || { });
5073
+
5074
+ var params = options.parameters, action = form.readAttribute('action') || '';
5075
+ if (action.blank()) action = window.location.href;
5076
+ options.parameters = form.serialize(true);
5077
+
5078
+ if (params) {
5079
+ if (Object.isString(params)) params = params.toQueryParams();
5080
+ Object.extend(options.parameters, params);
5081
+ }
5082
+
5083
+ if (form.hasAttribute('method') && !options.method)
5084
+ options.method = form.method;
5085
+
5086
+ return new Ajax.Request(action, options);
5087
+ }
5088
+ };
5089
+
5090
+ /*--------------------------------------------------------------------------*/
5091
+
5092
+
5093
+ Form.Element = {
5094
+ focus: function(element) {
5095
+ $(element).focus();
5096
+ return element;
5097
+ },
5098
+
5099
+ select: function(element) {
5100
+ $(element).select();
5101
+ return element;
5102
+ }
5103
+ };
5104
+
5105
+ Form.Element.Methods = {
5106
+
5107
+ serialize: function(element) {
5108
+ element = $(element);
5109
+ if (!element.disabled && element.name) {
5110
+ var value = element.getValue();
5111
+ if (value != undefined) {
5112
+ var pair = { };
5113
+ pair[element.name] = value;
5114
+ return Object.toQueryString(pair);
5115
+ }
5116
+ }
5117
+ return '';
5118
+ },
5119
+
5120
+ getValue: function(element) {
5121
+ element = $(element);
5122
+ var method = element.tagName.toLowerCase();
5123
+ return Form.Element.Serializers[method](element);
5124
+ },
5125
+
5126
+ setValue: function(element, value) {
5127
+ element = $(element);
5128
+ var method = element.tagName.toLowerCase();
5129
+ Form.Element.Serializers[method](element, value);
5130
+ return element;
5131
+ },
5132
+
5133
+ clear: function(element) {
5134
+ $(element).value = '';
5135
+ return element;
5136
+ },
5137
+
5138
+ present: function(element) {
5139
+ return $(element).value != '';
5140
+ },
5141
+
5142
+ activate: function(element) {
5143
+ element = $(element);
5144
+ try {
5145
+ element.focus();
5146
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
5147
+ !(/^(?:button|reset|submit)$/i.test(element.type))))
5148
+ element.select();
5149
+ } catch (e) { }
5150
+ return element;
5151
+ },
5152
+
5153
+ disable: function(element) {
5154
+ element = $(element);
5155
+ element.disabled = true;
5156
+ return element;
5157
+ },
5158
+
5159
+ enable: function(element) {
5160
+ element = $(element);
5161
+ element.disabled = false;
5162
+ return element;
5163
+ }
5164
+ };
5165
+
5166
+ /*--------------------------------------------------------------------------*/
5167
+
5168
+ var Field = Form.Element;
5169
+
5170
+ var $F = Form.Element.Methods.getValue;
5171
+
5172
+ /*--------------------------------------------------------------------------*/
5173
+
5174
+ Form.Element.Serializers = (function() {
5175
+ function input(element, value) {
5176
+ switch (element.type.toLowerCase()) {
5177
+ case 'checkbox':
5178
+ case 'radio':
5179
+ return inputSelector(element, value);
5180
+ default:
5181
+ return valueSelector(element, value);
5182
+ }
5183
+ }
5184
+
5185
+ function inputSelector(element, value) {
5186
+ if (Object.isUndefined(value))
5187
+ return element.checked ? element.value : null;
5188
+ else element.checked = !!value;
5189
+ }
5190
+
5191
+ function valueSelector(element, value) {
5192
+ if (Object.isUndefined(value)) return element.value;
5193
+ else element.value = value;
5194
+ }
5195
+
5196
+ function select(element, value) {
5197
+ if (Object.isUndefined(value))
5198
+ return (element.type === 'select-one' ? selectOne : selectMany)(element);
5199
+
5200
+ var opt, currentValue, single = !Object.isArray(value);
5201
+ for (var i = 0, length = element.length; i < length; i++) {
5202
+ opt = element.options[i];
5203
+ currentValue = this.optionValue(opt);
5204
+ if (single) {
5205
+ if (currentValue == value) {
5206
+ opt.selected = true;
5207
+ return;
5208
+ }
5209
+ }
5210
+ else opt.selected = value.include(currentValue);
5211
+ }
5212
+ }
5213
+
5214
+ function selectOne(element) {
5215
+ var index = element.selectedIndex;
5216
+ return index >= 0 ? optionValue(element.options[index]) : null;
5217
+ }
5218
+
5219
+ function selectMany(element) {
5220
+ var values, length = element.length;
5221
+ if (!length) return null;
5222
+
5223
+ for (var i = 0, values = []; i < length; i++) {
5224
+ var opt = element.options[i];
5225
+ if (opt.selected) values.push(optionValue(opt));
5226
+ }
5227
+ return values;
5228
+ }
5229
+
5230
+ function optionValue(opt) {
5231
+ return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
5232
+ }
5233
+
5234
+ return {
5235
+ input: input,
5236
+ inputSelector: inputSelector,
5237
+ textarea: valueSelector,
5238
+ select: select,
5239
+ selectOne: selectOne,
5240
+ selectMany: selectMany,
5241
+ optionValue: optionValue,
5242
+ button: valueSelector
5243
+ };
5244
+ })();
5245
+
5246
+ /*--------------------------------------------------------------------------*/
5247
+
5248
+
5249
+ Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
5250
+ initialize: function($super, element, frequency, callback) {
5251
+ $super(callback, frequency);
5252
+ this.element = $(element);
5253
+ this.lastValue = this.getValue();
5254
+ },
5255
+
5256
+ execute: function() {
5257
+ var value = this.getValue();
5258
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
5259
+ this.lastValue != value : String(this.lastValue) != String(value)) {
5260
+ this.callback(this.element, value);
5261
+ this.lastValue = value;
5262
+ }
5263
+ }
5264
+ });
5265
+
5266
+ Form.Element.Observer = Class.create(Abstract.TimedObserver, {
5267
+ getValue: function() {
5268
+ return Form.Element.getValue(this.element);
5269
+ }
5270
+ });
5271
+
5272
+ Form.Observer = Class.create(Abstract.TimedObserver, {
5273
+ getValue: function() {
5274
+ return Form.serialize(this.element);
5275
+ }
5276
+ });
5277
+
5278
+ /*--------------------------------------------------------------------------*/
5279
+
5280
+ Abstract.EventObserver = Class.create({
5281
+ initialize: function(element, callback) {
5282
+ this.element = $(element);
5283
+ this.callback = callback;
5284
+
5285
+ this.lastValue = this.getValue();
5286
+ if (this.element.tagName.toLowerCase() == 'form')
5287
+ this.registerFormCallbacks();
5288
+ else
5289
+ this.registerCallback(this.element);
5290
+ },
5291
+
5292
+ onElementEvent: function() {
5293
+ var value = this.getValue();
5294
+ if (this.lastValue != value) {
5295
+ this.callback(this.element, value);
5296
+ this.lastValue = value;
5297
+ }
5298
+ },
5299
+
5300
+ registerFormCallbacks: function() {
5301
+ Form.getElements(this.element).each(this.registerCallback, this);
5302
+ },
5303
+
5304
+ registerCallback: function(element) {
5305
+ if (element.type) {
5306
+ switch (element.type.toLowerCase()) {
5307
+ case 'checkbox':
5308
+ case 'radio':
5309
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
5310
+ break;
5311
+ default:
5312
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
5313
+ break;
5314
+ }
5315
+ }
5316
+ }
5317
+ });
5318
+
5319
+ Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
5320
+ getValue: function() {
5321
+ return Form.Element.getValue(this.element);
5322
+ }
5323
+ });
5324
+
5325
+ Form.EventObserver = Class.create(Abstract.EventObserver, {
5326
+ getValue: function() {
5327
+ return Form.serialize(this.element);
5328
+ }
5329
+ });
5330
+ (function() {
5331
+
5332
+ var Event = {
5333
+ KEY_BACKSPACE: 8,
5334
+ KEY_TAB: 9,
5335
+ KEY_RETURN: 13,
5336
+ KEY_ESC: 27,
5337
+ KEY_LEFT: 37,
5338
+ KEY_UP: 38,
5339
+ KEY_RIGHT: 39,
5340
+ KEY_DOWN: 40,
5341
+ KEY_DELETE: 46,
5342
+ KEY_HOME: 36,
5343
+ KEY_END: 35,
5344
+ KEY_PAGEUP: 33,
5345
+ KEY_PAGEDOWN: 34,
5346
+ KEY_INSERT: 45,
5347
+
5348
+ cache: {}
5349
+ };
5350
+
5351
+ var docEl = document.documentElement;
5352
+ var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
5353
+ && 'onmouseleave' in docEl;
5354
+
5355
+
5356
+
5357
+ var isIELegacyEvent = function(event) { return false; };
5358
+
5359
+ if (window.attachEvent) {
5360
+ if (window.addEventListener) {
5361
+ isIELegacyEvent = function(event) {
5362
+ return !(event instanceof window.Event);
5363
+ };
5364
+ } else {
5365
+ isIELegacyEvent = function(event) { return true; };
5366
+ }
5367
+ }
5368
+
5369
+ var _isButton;
5370
+
5371
+ function _isButtonForDOMEvents(event, code) {
5372
+ return event.which ? (event.which === code + 1) : (event.button === code);
5373
+ }
5374
+
5375
+ var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
5376
+ function _isButtonForLegacyEvents(event, code) {
5377
+ return event.button === legacyButtonMap[code];
5378
+ }
5379
+
5380
+ function _isButtonForWebKit(event, code) {
5381
+ switch (code) {
5382
+ case 0: return event.which == 1 && !event.metaKey;
5383
+ case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
5384
+ case 2: return event.which == 3;
5385
+ default: return false;
5386
+ }
5387
+ }
5388
+
5389
+ if (window.attachEvent) {
5390
+ if (!window.addEventListener) {
5391
+ _isButton = _isButtonForLegacyEvents;
5392
+ } else {
5393
+ _isButton = function(event, code) {
5394
+ return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
5395
+ _isButtonForDOMEvents(event, code);
5396
+ }
5397
+ }
5398
+ } else if (Prototype.Browser.WebKit) {
5399
+ _isButton = _isButtonForWebKit;
5400
+ } else {
5401
+ _isButton = _isButtonForDOMEvents;
5402
+ }
5403
+
5404
+ function isLeftClick(event) { return _isButton(event, 0) }
5405
+
5406
+ function isMiddleClick(event) { return _isButton(event, 1) }
5407
+
5408
+ function isRightClick(event) { return _isButton(event, 2) }
5409
+
5410
+ function element(event) {
5411
+ event = Event.extend(event);
5412
+
5413
+ var node = event.target, type = event.type,
5414
+ currentTarget = event.currentTarget;
5415
+
5416
+ if (currentTarget && currentTarget.tagName) {
5417
+ if (type === 'load' || type === 'error' ||
5418
+ (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
5419
+ && currentTarget.type === 'radio'))
5420
+ node = currentTarget;
5421
+ }
5422
+
5423
+ if (node.nodeType == Node.TEXT_NODE)
5424
+ node = node.parentNode;
5425
+
5426
+ return Element.extend(node);
5427
+ }
5428
+
5429
+ function findElement(event, expression) {
5430
+ var element = Event.element(event);
5431
+
5432
+ if (!expression) return element;
5433
+ while (element) {
5434
+ if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
5435
+ return Element.extend(element);
5436
+ }
5437
+ element = element.parentNode;
5438
+ }
5439
+ }
5440
+
5441
+ function pointer(event) {
5442
+ return { x: pointerX(event), y: pointerY(event) };
5443
+ }
5444
+
5445
+ function pointerX(event) {
5446
+ var docElement = document.documentElement,
5447
+ body = document.body || { scrollLeft: 0 };
5448
+
5449
+ return event.pageX || (event.clientX +
5450
+ (docElement.scrollLeft || body.scrollLeft) -
5451
+ (docElement.clientLeft || 0));
5452
+ }
5453
+
5454
+ function pointerY(event) {
5455
+ var docElement = document.documentElement,
5456
+ body = document.body || { scrollTop: 0 };
5457
+
5458
+ return event.pageY || (event.clientY +
5459
+ (docElement.scrollTop || body.scrollTop) -
5460
+ (docElement.clientTop || 0));
5461
+ }
5462
+
5463
+
5464
+ function stop(event) {
5465
+ Event.extend(event);
5466
+ event.preventDefault();
5467
+ event.stopPropagation();
5468
+
5469
+ event.stopped = true;
5470
+ }
5471
+
5472
+
5473
+ Event.Methods = {
5474
+ isLeftClick: isLeftClick,
5475
+ isMiddleClick: isMiddleClick,
5476
+ isRightClick: isRightClick,
5477
+
5478
+ element: element,
5479
+ findElement: findElement,
5480
+
5481
+ pointer: pointer,
5482
+ pointerX: pointerX,
5483
+ pointerY: pointerY,
5484
+
5485
+ stop: stop
5486
+ };
5487
+
5488
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
5489
+ m[name] = Event.Methods[name].methodize();
5490
+ return m;
5491
+ });
5492
+
5493
+ if (window.attachEvent) {
5494
+ function _relatedTarget(event) {
5495
+ var element;
5496
+ switch (event.type) {
5497
+ case 'mouseover':
5498
+ case 'mouseenter':
5499
+ element = event.fromElement;
5500
+ break;
5501
+ case 'mouseout':
5502
+ case 'mouseleave':
5503
+ element = event.toElement;
5504
+ break;
5505
+ default:
5506
+ return null;
5507
+ }
5508
+ return Element.extend(element);
5509
+ }
5510
+
5511
+ var additionalMethods = {
5512
+ stopPropagation: function() { this.cancelBubble = true },
5513
+ preventDefault: function() { this.returnValue = false },
5514
+ inspect: function() { return '[object Event]' }
5515
+ };
5516
+
5517
+ Event.extend = function(event, element) {
5518
+ if (!event) return false;
5519
+
5520
+ if (!isIELegacyEvent(event)) return event;
5521
+
5522
+ if (event._extendedByPrototype) return event;
5523
+ event._extendedByPrototype = Prototype.emptyFunction;
5524
+
5525
+ var pointer = Event.pointer(event);
5526
+
5527
+ Object.extend(event, {
5528
+ target: event.srcElement || element,
5529
+ relatedTarget: _relatedTarget(event),
5530
+ pageX: pointer.x,
5531
+ pageY: pointer.y
5532
+ });
5533
+
5534
+ Object.extend(event, methods);
5535
+ Object.extend(event, additionalMethods);
5536
+
5537
+ return event;
5538
+ };
5539
+ } else {
5540
+ Event.extend = Prototype.K;
5541
+ }
5542
+
5543
+ if (window.addEventListener) {
5544
+ Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
5545
+ Object.extend(Event.prototype, methods);
5546
+ }
5547
+
5548
+ function _createResponder(element, eventName, handler) {
5549
+ var registry = Element.retrieve(element, 'prototype_event_registry');
5550
+
5551
+ if (Object.isUndefined(registry)) {
5552
+ CACHE.push(element);
5553
+ registry = Element.retrieve(element, 'prototype_event_registry', $H());
5554
+ }
5555
+
5556
+ var respondersForEvent = registry.get(eventName);
5557
+ if (Object.isUndefined(respondersForEvent)) {
5558
+ respondersForEvent = [];
5559
+ registry.set(eventName, respondersForEvent);
5560
+ }
5561
+
5562
+ if (respondersForEvent.pluck('handler').include(handler)) return false;
5563
+
5564
+ var responder;
5565
+ if (eventName.include(":")) {
5566
+ responder = function(event) {
5567
+ if (Object.isUndefined(event.eventName))
5568
+ return false;
5569
+
5570
+ if (event.eventName !== eventName)
5571
+ return false;
5572
+
5573
+ Event.extend(event, element);
5574
+ handler.call(element, event);
5575
+ };
5576
+ } else {
5577
+ if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
5578
+ (eventName === "mouseenter" || eventName === "mouseleave")) {
5579
+ if (eventName === "mouseenter" || eventName === "mouseleave") {
5580
+ responder = function(event) {
5581
+ Event.extend(event, element);
5582
+
5583
+ var parent = event.relatedTarget;
5584
+ while (parent && parent !== element) {
5585
+ try { parent = parent.parentNode; }
5586
+ catch(e) { parent = element; }
5587
+ }
5588
+
5589
+ if (parent === element) return;
5590
+
5591
+ handler.call(element, event);
5592
+ };
5593
+ }
5594
+ } else {
5595
+ responder = function(event) {
5596
+ Event.extend(event, element);
5597
+ handler.call(element, event);
5598
+ };
5599
+ }
5600
+ }
5601
+
5602
+ responder.handler = handler;
5603
+ respondersForEvent.push(responder);
5604
+ return responder;
5605
+ }
5606
+
5607
+ function _destroyCache() {
5608
+ for (var i = 0, length = CACHE.length; i < length; i++) {
5609
+ Event.stopObserving(CACHE[i]);
5610
+ CACHE[i] = null;
5611
+ }
5612
+ }
5613
+
5614
+ var CACHE = [];
5615
+
5616
+ if (Prototype.Browser.IE)
5617
+ window.attachEvent('onunload', _destroyCache);
5618
+
5619
+ if (Prototype.Browser.WebKit)
5620
+ window.addEventListener('unload', Prototype.emptyFunction, false);
5621
+
5622
+
5623
+ var _getDOMEventName = Prototype.K,
5624
+ translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
5625
+
5626
+ if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
5627
+ _getDOMEventName = function(eventName) {
5628
+ return (translations[eventName] || eventName);
5629
+ };
5630
+ }
5631
+
5632
+ function observe(element, eventName, handler) {
5633
+ element = $(element);
5634
+
5635
+ var responder = _createResponder(element, eventName, handler);
5636
+
5637
+ if (!responder) return element;
5638
+
5639
+ if (eventName.include(':')) {
5640
+ if (element.addEventListener)
5641
+ element.addEventListener("dataavailable", responder, false);
5642
+ else {
5643
+ element.attachEvent("ondataavailable", responder);
5644
+ element.attachEvent("onlosecapture", responder);
5645
+ }
5646
+ } else {
5647
+ var actualEventName = _getDOMEventName(eventName);
5648
+
5649
+ if (element.addEventListener)
5650
+ element.addEventListener(actualEventName, responder, false);
5651
+ else
5652
+ element.attachEvent("on" + actualEventName, responder);
5653
+ }
5654
+
5655
+ return element;
5656
+ }
5657
+
5658
+ function stopObserving(element, eventName, handler) {
5659
+ element = $(element);
5660
+
5661
+ var registry = Element.retrieve(element, 'prototype_event_registry');
5662
+ if (!registry) return element;
5663
+
5664
+ if (!eventName) {
5665
+ registry.each( function(pair) {
5666
+ var eventName = pair.key;
5667
+ stopObserving(element, eventName);
5668
+ });
5669
+ return element;
5670
+ }
5671
+
5672
+ var responders = registry.get(eventName);
5673
+ if (!responders) return element;
5674
+
5675
+ if (!handler) {
5676
+ responders.each(function(r) {
5677
+ stopObserving(element, eventName, r.handler);
5678
+ });
5679
+ return element;
5680
+ }
5681
+
5682
+ var i = responders.length, responder;
5683
+ while (i--) {
5684
+ if (responders[i].handler === handler) {
5685
+ responder = responders[i];
5686
+ break;
5687
+ }
5688
+ }
5689
+ if (!responder) return element;
5690
+
5691
+ if (eventName.include(':')) {
5692
+ if (element.removeEventListener)
5693
+ element.removeEventListener("dataavailable", responder, false);
5694
+ else {
5695
+ element.detachEvent("ondataavailable", responder);
5696
+ element.detachEvent("onlosecapture", responder);
5697
+ }
5698
+ } else {
5699
+ var actualEventName = _getDOMEventName(eventName);
5700
+ if (element.removeEventListener)
5701
+ element.removeEventListener(actualEventName, responder, false);
5702
+ else
5703
+ element.detachEvent('on' + actualEventName, responder);
5704
+ }
5705
+
5706
+ registry.set(eventName, responders.without(responder));
5707
+
5708
+ return element;
5709
+ }
5710
+
5711
+ function fire(element, eventName, memo, bubble) {
5712
+ element = $(element);
5713
+
5714
+ if (Object.isUndefined(bubble))
5715
+ bubble = true;
5716
+
5717
+ if (element == document && document.createEvent && !element.dispatchEvent)
5718
+ element = document.documentElement;
5719
+
5720
+ var event;
5721
+ if (document.createEvent) {
5722
+ event = document.createEvent('HTMLEvents');
5723
+ event.initEvent('dataavailable', bubble, true);
5724
+ } else {
5725
+ event = document.createEventObject();
5726
+ event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
5727
+ }
5728
+
5729
+ event.eventName = eventName;
5730
+ event.memo = memo || { };
5731
+
5732
+ if (document.createEvent)
5733
+ element.dispatchEvent(event);
5734
+ else
5735
+ element.fireEvent(event.eventType, event);
5736
+
5737
+ return Event.extend(event);
5738
+ }
5739
+
5740
+ Event.Handler = Class.create({
5741
+ initialize: function(element, eventName, selector, callback) {
5742
+ this.element = $(element);
5743
+ this.eventName = eventName;
5744
+ this.selector = selector;
5745
+ this.callback = callback;
5746
+ this.handler = this.handleEvent.bind(this);
5747
+ },
5748
+
5749
+ start: function() {
5750
+ Event.observe(this.element, this.eventName, this.handler);
5751
+ return this;
5752
+ },
5753
+
5754
+ stop: function() {
5755
+ Event.stopObserving(this.element, this.eventName, this.handler);
5756
+ return this;
5757
+ },
5758
+
5759
+ handleEvent: function(event) {
5760
+ var element = Event.findElement(event, this.selector);
5761
+ if (element) this.callback.call(this.element, event, element);
5762
+ }
5763
+ });
5764
+
5765
+ function on(element, eventName, selector, callback) {
5766
+ element = $(element);
5767
+ if (Object.isFunction(selector) && Object.isUndefined(callback)) {
5768
+ callback = selector, selector = null;
5769
+ }
5770
+
5771
+ return new Event.Handler(element, eventName, selector, callback).start();
5772
+ }
5773
+
5774
+ Object.extend(Event, Event.Methods);
5775
+
5776
+ Object.extend(Event, {
5777
+ fire: fire,
5778
+ observe: observe,
5779
+ stopObserving: stopObserving,
5780
+ on: on
5781
+ });
5782
+
5783
+ Element.addMethods({
5784
+ fire: fire,
5785
+
5786
+ observe: observe,
5787
+
5788
+ stopObserving: stopObserving,
5789
+
5790
+ on: on
5791
+ });
5792
+
5793
+ Object.extend(document, {
5794
+ fire: fire.methodize(),
5795
+
5796
+ observe: observe.methodize(),
5797
+
5798
+ stopObserving: stopObserving.methodize(),
5799
+
5800
+ on: on.methodize(),
5801
+
5802
+ loaded: false
5803
+ });
5804
+
5805
+ if (window.Event) Object.extend(window.Event, Event);
5806
+ else window.Event = Event;
5807
+ })();
5808
+
5809
+ (function() {
5810
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
5811
+ Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
5812
+
5813
+ var timer;
5814
+
5815
+ function fireContentLoadedEvent() {
5816
+ if (document.loaded) return;
5817
+ if (timer) window.clearTimeout(timer);
5818
+ document.loaded = true;
5819
+ document.fire('dom:loaded');
5820
+ }
5821
+
5822
+ function checkReadyState() {
5823
+ if (document.readyState === 'complete') {
5824
+ document.stopObserving('readystatechange', checkReadyState);
5825
+ fireContentLoadedEvent();
5826
+ }
5827
+ }
5828
+
5829
+ function pollDoScroll() {
5830
+ try { document.documentElement.doScroll('left'); }
5831
+ catch(e) {
5832
+ timer = pollDoScroll.defer();
5833
+ return;
5834
+ }
5835
+ fireContentLoadedEvent();
5836
+ }
5837
+
5838
+ if (document.addEventListener) {
5839
+ document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
5840
+ } else {
5841
+ document.observe('readystatechange', checkReadyState);
5842
+ if (window == top)
5843
+ timer = pollDoScroll.defer();
5844
+ }
5845
+
5846
+ Event.observe(window, 'load', fireContentLoadedEvent);
5847
+ })();
5848
+
5849
+
5850
+ Element.addMethods();
5851
+ /*------------------------------- DEPRECATED -------------------------------*/
5852
+
5853
+ Hash.toQueryString = Object.toQueryString;
5854
+
5855
+ var Toggle = { display: Element.toggle };
5856
+
5857
+ Element.Methods.childOf = Element.Methods.descendantOf;
5858
+
5859
+ var Insertion = {
5860
+ Before: function(element, content) {
5861
+ return Element.insert(element, {before:content});
5862
+ },
5863
+
5864
+ Top: function(element, content) {
5865
+ return Element.insert(element, {top:content});
5866
+ },
5867
+
5868
+ Bottom: function(element, content) {
5869
+ return Element.insert(element, {bottom:content});
5870
+ },
5871
+
5872
+ After: function(element, content) {
5873
+ return Element.insert(element, {after:content});
5874
+ }
5875
+ };
5876
+
5877
+ var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
5878
+
5879
+ var Position = {
5880
+ includeScrollOffsets: false,
5881
+
5882
+ prepare: function() {
5883
+ this.deltaX = window.pageXOffset
5884
+ || document.documentElement.scrollLeft
5885
+ || document.body.scrollLeft
5886
+ || 0;
5887
+ this.deltaY = window.pageYOffset
5888
+ || document.documentElement.scrollTop
5889
+ || document.body.scrollTop
5890
+ || 0;
5891
+ },
5892
+
5893
+ within: function(element, x, y) {
5894
+ if (this.includeScrollOffsets)
5895
+ return this.withinIncludingScrolloffsets(element, x, y);
5896
+ this.xcomp = x;
5897
+ this.ycomp = y;
5898
+ this.offset = Element.cumulativeOffset(element);
5899
+
5900
+ return (y >= this.offset[1] &&
5901
+ y < this.offset[1] + element.offsetHeight &&
5902
+ x >= this.offset[0] &&
5903
+ x < this.offset[0] + element.offsetWidth);
5904
+ },
5905
+
5906
+ withinIncludingScrolloffsets: function(element, x, y) {
5907
+ var offsetcache = Element.cumulativeScrollOffset(element);
5908
+
5909
+ this.xcomp = x + offsetcache[0] - this.deltaX;
5910
+ this.ycomp = y + offsetcache[1] - this.deltaY;
5911
+ this.offset = Element.cumulativeOffset(element);
5912
+
5913
+ return (this.ycomp >= this.offset[1] &&
5914
+ this.ycomp < this.offset[1] + element.offsetHeight &&
5915
+ this.xcomp >= this.offset[0] &&
5916
+ this.xcomp < this.offset[0] + element.offsetWidth);
5917
+ },
5918
+
5919
+ overlap: function(mode, element) {
5920
+ if (!mode) return 0;
5921
+ if (mode == 'vertical')
5922
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
5923
+ element.offsetHeight;
5924
+ if (mode == 'horizontal')
5925
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
5926
+ element.offsetWidth;
5927
+ },
5928
+
5929
+
5930
+ cumulativeOffset: Element.Methods.cumulativeOffset,
5931
+
5932
+ positionedOffset: Element.Methods.positionedOffset,
5933
+
5934
+ absolutize: function(element) {
5935
+ Position.prepare();
5936
+ return Element.absolutize(element);
5937
+ },
5938
+
5939
+ relativize: function(element) {
5940
+ Position.prepare();
5941
+ return Element.relativize(element);
5942
+ },
5943
+
5944
+ realOffset: Element.Methods.cumulativeScrollOffset,
5945
+
5946
+ offsetParent: Element.Methods.getOffsetParent,
5947
+
5948
+ page: Element.Methods.viewportOffset,
5949
+
5950
+ clone: function(source, target, options) {
5951
+ options = options || { };
5952
+ return Element.clonePosition(target, source, options);
5953
+ }
5954
+ };
5955
+
5956
+ /*--------------------------------------------------------------------------*/
5957
+
5958
+ if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
5959
+ function iter(name) {
5960
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
5961
+ }
5962
+
5963
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
5964
+ function(element, className) {
5965
+ className = className.toString().strip();
5966
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
5967
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
5968
+ } : function(element, className) {
5969
+ className = className.toString().strip();
5970
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
5971
+ if (!classNames && !className) return elements;
5972
+
5973
+ var nodes = $(element).getElementsByTagName('*');
5974
+ className = ' ' + className + ' ';
5975
+
5976
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
5977
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
5978
+ (classNames && classNames.all(function(name) {
5979
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
5980
+ }))))
5981
+ elements.push(Element.extend(child));
5982
+ }
5983
+ return elements;
5984
+ };
5985
+
5986
+ return function(className, parentElement) {
5987
+ return $(parentElement || document.body).getElementsByClassName(className);
5988
+ };
5989
+ }(Element.Methods);
5990
+
5991
+ /*--------------------------------------------------------------------------*/
5992
+
5993
+ Element.ClassNames = Class.create();
5994
+ Element.ClassNames.prototype = {
5995
+ initialize: function(element) {
5996
+ this.element = $(element);
5997
+ },
5998
+
5999
+ _each: function(iterator) {
6000
+ this.element.className.split(/\s+/).select(function(name) {
6001
+ return name.length > 0;
6002
+ })._each(iterator);
6003
+ },
6004
+
6005
+ set: function(className) {
6006
+ this.element.className = className;
6007
+ },
6008
+
6009
+ add: function(classNameToAdd) {
6010
+ if (this.include(classNameToAdd)) return;
6011
+ this.set($A(this).concat(classNameToAdd).join(' '));
6012
+ },
6013
+
6014
+ remove: function(classNameToRemove) {
6015
+ if (!this.include(classNameToRemove)) return;
6016
+ this.set($A(this).without(classNameToRemove).join(' '));
6017
+ },
6018
+
6019
+ toString: function() {
6020
+ return $A(this).join(' ');
6021
+ }
6022
+ };
6023
+
6024
+ Object.extend(Element.ClassNames.prototype, Enumerable);
6025
+
6026
+ /*--------------------------------------------------------------------------*/
6027
+
6028
+ (function() {
6029
+ window.Selector = Class.create({
6030
+ initialize: function(expression) {
6031
+ this.expression = expression.strip();
6032
+ },
6033
+
6034
+ findElements: function(rootElement) {
6035
+ return Prototype.Selector.select(this.expression, rootElement);
6036
+ },
6037
+
6038
+ match: function(element) {
6039
+ return Prototype.Selector.match(element, this.expression);
6040
+ },
6041
+
6042
+ toString: function() {
6043
+ return this.expression;
6044
+ },
6045
+
6046
+ inspect: function() {
6047
+ return "#<Selector: " + this.expression + ">";
6048
+ }
6049
+ });
6050
+
6051
+ Object.extend(Selector, {
6052
+ matchElements: function(elements, expression) {
6053
+ var match = Prototype.Selector.match,
6054
+ results = [];
6055
+
6056
+ for (var i = 0, length = elements.length; i < length; i++) {
6057
+ var element = elements[i];
6058
+ if (match(element, expression)) {
6059
+ results.push(Element.extend(element));
6060
+ }
6061
+ }
6062
+ return results;
6063
+ },
6064
+
6065
+ findElement: function(elements, expression, index) {
6066
+ index = index || 0;
6067
+ var matchIndex = 0, element;
6068
+ for (var i = 0, length = elements.length; i < length; i++) {
6069
+ element = elements[i];
6070
+ if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
6071
+ return Element.extend(element);
6072
+ }
6073
+ }
6074
+ },
6075
+
6076
+ findChildElements: function(element, expressions) {
6077
+ var selector = expressions.toArray().join(', ');
6078
+ return Prototype.Selector.select(selector, element || document);
6079
+ }
6080
+ });
6081
+ })();
js/mageho/sortproducts/scriptaculous.js ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // script.aculo.us scriptaculous.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
2
+
3
+ // Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ //
5
+ // Permission is hereby granted, free of charge, to any person obtaining
6
+ // a copy of this software and associated documentation files (the
7
+ // "Software"), to deal in the Software without restriction, including
8
+ // without limitation the rights to use, copy, modify, merge, publish,
9
+ // distribute, sublicense, and/or sell copies of the Software, and to
10
+ // permit persons to whom the Software is furnished to do so, subject to
11
+ // the following conditions:
12
+ //
13
+ // The above copyright notice and this permission notice shall be
14
+ // included in all copies or substantial portions of the Software.
15
+ //
16
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ //
24
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
25
+
26
+ var Scriptaculous = {
27
+ Version: '1.9.0',
28
+ require: function(libraryName) {
29
+ try{
30
+ // inserting via DOM fails in Safari 2.0, so brute force approach
31
+ document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
32
+ } catch(e) {
33
+ // for xhtml+xml served content, fall back to DOM methods
34
+ var script = document.createElement('script');
35
+ script.type = 'text/javascript';
36
+ script.src = libraryName;
37
+ document.getElementsByTagName('head')[0].appendChild(script);
38
+ }
39
+ },
40
+ REQUIRED_PROTOTYPE: '1.6.0.3',
41
+ load: function() {
42
+ function convertVersionString(versionString) {
43
+ var v = versionString.replace(/_.*|\./g, '');
44
+ v = parseInt(v + '0'.times(4-v.length));
45
+ return versionString.indexOf('_') > -1 ? v-1 : v;
46
+ }
47
+
48
+ if((typeof Prototype=='undefined') ||
49
+ (typeof Element == 'undefined') ||
50
+ (typeof Element.Methods=='undefined') ||
51
+ (convertVersionString(Prototype.Version) <
52
+ convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
53
+ throw("script.aculo.us requires the Prototype JavaScript framework >= " +
54
+ Scriptaculous.REQUIRED_PROTOTYPE);
55
+
56
+ var js = /scriptaculous\.js(\?.*)?$/;
57
+ $$('script[src]').findAll(function(s) {
58
+ return s.src.match(js);
59
+ }).each(function(s) {
60
+ var path = s.src.replace(js, ''),
61
+ includes = s.src.match(/\?.*load=([a-z,]*)/);
62
+ (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
63
+ function(include) { Scriptaculous.require(path+include+'.js') });
64
+ });
65
+ }
66
+ };
67
+
68
+ Scriptaculous.load();
js/mageho/sortproducts/slider.js ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // script.aculo.us slider.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
2
+
3
+ // Copyright (c) 2005-2010 Marty Haught, Thomas Fuchs
4
+ //
5
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
6
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
7
+
8
+ if (!Control) var Control = { };
9
+
10
+ // options:
11
+ // axis: 'vertical', or 'horizontal' (default)
12
+ //
13
+ // callbacks:
14
+ // onChange(value)
15
+ // onSlide(value)
16
+ Control.Slider = Class.create({
17
+ initialize: function(handle, track, options) {
18
+ var slider = this;
19
+
20
+ if (Object.isArray(handle)) {
21
+ this.handles = handle.collect( function(e) { return $(e) });
22
+ } else {
23
+ this.handles = [$(handle)];
24
+ }
25
+
26
+ this.track = $(track);
27
+ this.options = options || { };
28
+
29
+ this.axis = this.options.axis || 'horizontal';
30
+ this.increment = this.options.increment || 1;
31
+ this.step = parseInt(this.options.step || '1');
32
+ this.range = this.options.range || $R(0,1);
33
+
34
+ this.value = 0; // assure backwards compat
35
+ this.values = this.handles.map( function() { return 0 });
36
+ this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
37
+ this.options.startSpan = $(this.options.startSpan || null);
38
+ this.options.endSpan = $(this.options.endSpan || null);
39
+
40
+ this.restricted = this.options.restricted || false;
41
+
42
+ this.maximum = this.options.maximum || this.range.end;
43
+ this.minimum = this.options.minimum || this.range.start;
44
+
45
+ // Will be used to align the handle onto the track, if necessary
46
+ this.alignX = parseInt(this.options.alignX || '0');
47
+ this.alignY = parseInt(this.options.alignY || '0');
48
+
49
+ this.trackLength = this.maximumOffset() - this.minimumOffset();
50
+
51
+ this.handleLength = this.isVertical() ?
52
+ (this.handles[0].offsetHeight != 0 ?
53
+ this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
54
+ (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
55
+ this.handles[0].style.width.replace(/px$/,""));
56
+
57
+ this.active = false;
58
+ this.dragging = false;
59
+ this.disabled = false;
60
+
61
+ if (this.options.disabled) this.setDisabled();
62
+
63
+ // Allowed values array
64
+ this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
65
+ if (this.allowedValues) {
66
+ this.minimum = this.allowedValues.min();
67
+ this.maximum = this.allowedValues.max();
68
+ }
69
+
70
+ this.eventMouseDown = this.startDrag.bindAsEventListener(this);
71
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
72
+ this.eventMouseMove = this.update.bindAsEventListener(this);
73
+
74
+ // Initialize handles in reverse (make sure first handle is active)
75
+ this.handles.each( function(h,i) {
76
+ i = slider.handles.length-1-i;
77
+ slider.setValue(parseFloat(
78
+ (Object.isArray(slider.options.sliderValue) ?
79
+ slider.options.sliderValue[i] : slider.options.sliderValue) ||
80
+ slider.range.start), i);
81
+ h.makePositioned().observe("mousedown", slider.eventMouseDown);
82
+ });
83
+
84
+ this.track.observe("mousedown", this.eventMouseDown);
85
+ document.observe("mouseup", this.eventMouseUp);
86
+ document.observe("mousemove", this.eventMouseMove);
87
+
88
+ this.initialized = true;
89
+ },
90
+ dispose: function() {
91
+ var slider = this;
92
+ Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
93
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
94
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
95
+ this.handles.each( function(h) {
96
+ Event.stopObserving(h, "mousedown", slider.eventMouseDown);
97
+ });
98
+ },
99
+ setDisabled: function(){
100
+ this.disabled = true;
101
+ },
102
+ setEnabled: function(){
103
+ this.disabled = false;
104
+ },
105
+ getNearestValue: function(value){
106
+ if (this.allowedValues){
107
+ if (value >= this.allowedValues.max()) return(this.allowedValues.max());
108
+ if (value <= this.allowedValues.min()) return(this.allowedValues.min());
109
+
110
+ var offset = Math.abs(this.allowedValues[0] - value);
111
+ var newValue = this.allowedValues[0];
112
+ this.allowedValues.each( function(v) {
113
+ var currentOffset = Math.abs(v - value);
114
+ if (currentOffset <= offset){
115
+ newValue = v;
116
+ offset = currentOffset;
117
+ }
118
+ });
119
+ return newValue;
120
+ }
121
+ if (value > this.range.end) return this.range.end;
122
+ if (value < this.range.start) return this.range.start;
123
+ return value;
124
+ },
125
+ setValue: function(sliderValue, handleIdx){
126
+ if (!this.active) {
127
+ this.activeHandleIdx = handleIdx || 0;
128
+ this.activeHandle = this.handles[this.activeHandleIdx];
129
+ this.updateStyles();
130
+ }
131
+ handleIdx = handleIdx || this.activeHandleIdx || 0;
132
+ if (this.initialized && this.restricted) {
133
+ if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
134
+ sliderValue = this.values[handleIdx-1];
135
+ if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
136
+ sliderValue = this.values[handleIdx+1];
137
+ }
138
+ sliderValue = this.getNearestValue(sliderValue);
139
+ this.values[handleIdx] = sliderValue;
140
+ this.value = this.values[0]; // assure backwards compat
141
+
142
+ this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
143
+ this.translateToPx(sliderValue);
144
+
145
+ this.drawSpans();
146
+ if (!this.dragging || !this.event) this.updateFinished();
147
+ },
148
+ setValueBy: function(delta, handleIdx) {
149
+ this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
150
+ handleIdx || this.activeHandleIdx || 0);
151
+ },
152
+ translateToPx: function(value) {
153
+ return Math.round(
154
+ ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
155
+ (value - this.range.start)) + "px";
156
+ },
157
+ translateToValue: function(offset) {
158
+ return ((offset/(this.trackLength-this.handleLength) *
159
+ (this.range.end-this.range.start)) + this.range.start);
160
+ },
161
+ getRange: function(range) {
162
+ var v = this.values.sortBy(Prototype.K);
163
+ range = range || 0;
164
+ return $R(v[range],v[range+1]);
165
+ },
166
+ minimumOffset: function(){
167
+ return(this.isVertical() ? this.alignY : this.alignX);
168
+ },
169
+ maximumOffset: function(){
170
+ return(this.isVertical() ?
171
+ (this.track.offsetHeight != 0 ? this.track.offsetHeight :
172
+ this.track.style.height.replace(/px$/,"")) - this.alignY :
173
+ (this.track.offsetWidth != 0 ? this.track.offsetWidth :
174
+ this.track.style.width.replace(/px$/,"")) - this.alignX);
175
+ },
176
+ isVertical: function(){
177
+ return (this.axis == 'vertical');
178
+ },
179
+ drawSpans: function() {
180
+ var slider = this;
181
+ if (this.spans)
182
+ $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
183
+ if (this.options.startSpan)
184
+ this.setSpan(this.options.startSpan,
185
+ $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
186
+ if (this.options.endSpan)
187
+ this.setSpan(this.options.endSpan,
188
+ $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
189
+ },
190
+ setSpan: function(span, range) {
191
+ if (this.isVertical()) {
192
+ span.style.top = this.translateToPx(range.start);
193
+ span.style.height = this.translateToPx(range.end - range.start + this.range.start);
194
+ } else {
195
+ span.style.left = this.translateToPx(range.start);
196
+ span.style.width = this.translateToPx(range.end - range.start + this.range.start);
197
+ }
198
+ },
199
+ updateStyles: function() {
200
+ this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
201
+ Element.addClassName(this.activeHandle, 'selected');
202
+ },
203
+ startDrag: function(event) {
204
+ if (Event.isLeftClick(event)) {
205
+ if (!this.disabled){
206
+ this.active = true;
207
+
208
+ var handle = Event.element(event);
209
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
210
+ var track = handle;
211
+ if (track==this.track) {
212
+ var offsets = this.track.cumulativeOffset();
213
+ this.event = event;
214
+ this.setValue(this.translateToValue(
215
+ (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
216
+ ));
217
+ var offsets = this.activeHandle.cumulativeOffset();
218
+ this.offsetX = (pointer[0] - offsets[0]);
219
+ this.offsetY = (pointer[1] - offsets[1]);
220
+ } else {
221
+ // find the handle (prevents issues with Safari)
222
+ while((this.handles.indexOf(handle) == -1) && handle.parentNode)
223
+ handle = handle.parentNode;
224
+
225
+ if (this.handles.indexOf(handle)!=-1) {
226
+ this.activeHandle = handle;
227
+ this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
228
+ this.updateStyles();
229
+
230
+ var offsets = this.activeHandle.cumulativeOffset();
231
+ this.offsetX = (pointer[0] - offsets[0]);
232
+ this.offsetY = (pointer[1] - offsets[1]);
233
+ }
234
+ }
235
+ }
236
+ Event.stop(event);
237
+ }
238
+ },
239
+ update: function(event) {
240
+ if (this.active) {
241
+ if (!this.dragging) this.dragging = true;
242
+ this.draw(event);
243
+ if (Prototype.Browser.WebKit) window.scrollBy(0,0);
244
+ Event.stop(event);
245
+ }
246
+ },
247
+ draw: function(event) {
248
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
249
+ var offsets = this.track.cumulativeOffset();
250
+ pointer[0] -= this.offsetX + offsets[0];
251
+ pointer[1] -= this.offsetY + offsets[1];
252
+ this.event = event;
253
+ this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
254
+ if (this.initialized && this.options.onSlide)
255
+ this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
256
+ },
257
+ endDrag: function(event) {
258
+ if (this.active && this.dragging) {
259
+ this.finishDrag(event, true);
260
+ Event.stop(event);
261
+ }
262
+ this.active = false;
263
+ this.dragging = false;
264
+ },
265
+ finishDrag: function(event, success) {
266
+ this.active = false;
267
+ this.dragging = false;
268
+ this.updateFinished();
269
+ },
270
+ updateFinished: function() {
271
+ if (this.initialized && this.options.onChange)
272
+ this.options.onChange(this.values.length>1 ? this.values : this.value, this);
273
+ this.event = null;
274
+ }
275
+ });
js/mageho/sortproducts/sound.js ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // script.aculo.us sound.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
2
+
3
+ // Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ //
5
+ // Based on code created by Jules Gravinese (http://www.webveteran.com/)
6
+ //
7
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
8
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
9
+
10
+ Sound = {
11
+ tracks: {},
12
+ _enabled: true,
13
+ template:
14
+ new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
15
+ enable: function(){
16
+ Sound._enabled = true;
17
+ },
18
+ disable: function(){
19
+ Sound._enabled = false;
20
+ },
21
+ play: function(url){
22
+ if(!Sound._enabled) return;
23
+ var options = Object.extend({
24
+ track: 'global', url: url, replace: false
25
+ }, arguments[1] || {});
26
+
27
+ if(options.replace && this.tracks[options.track]) {
28
+ $R(0, this.tracks[options.track].id).each(function(id){
29
+ var sound = $('sound_'+options.track+'_'+id);
30
+ sound.Stop && sound.Stop();
31
+ sound.remove();
32
+ });
33
+ this.tracks[options.track] = null;
34
+ }
35
+
36
+ if(!this.tracks[options.track])
37
+ this.tracks[options.track] = { id: 0 };
38
+ else
39
+ this.tracks[options.track].id++;
40
+
41
+ options.id = this.tracks[options.track].id;
42
+ $$('body')[0].insert(
43
+ Prototype.Browser.IE ? new Element('bgsound',{
44
+ id: 'sound_'+options.track+'_'+options.id,
45
+ src: options.url, loop: 1, autostart: true
46
+ }) : Sound.template.evaluate(options));
47
+ }
48
+ };
49
+
50
+ if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
51
+ if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
52
+ Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>');
53
+ else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('Windows Media') != -1 }))
54
+ Sound.template = new Template('<object id="sound_#{track}_#{id}" type="application/x-mplayer2" data="#{url}"></object>');
55
+ else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('RealPlayer') != -1 }))
56
+ Sound.template = new Template('<embed type="audio/x-pn-realaudio-plugin" style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>');
57
+ else
58
+ Sound.play = function(){};
59
+ }
js/mageho/sortproducts/unittest.js ADDED
@@ -0,0 +1,568 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // script.aculo.us unittest.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
2
+
3
+ // Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
4
+ // (c) 2005-2010 Jon Tirsen (http://www.tirsen.com)
5
+ // (c) 2005-2010 Michael Schuerig (http://www.schuerig.de/michael/)
6
+ //
7
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
8
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
9
+
10
+ // experimental, Firefox-only
11
+ Event.simulateMouse = function(element, eventName) {
12
+ var options = Object.extend({
13
+ pointerX: 0,
14
+ pointerY: 0,
15
+ buttons: 0,
16
+ ctrlKey: false,
17
+ altKey: false,
18
+ shiftKey: false,
19
+ metaKey: false
20
+ }, arguments[2] || {});
21
+ var oEvent = document.createEvent("MouseEvents");
22
+ oEvent.initMouseEvent(eventName, true, true, document.defaultView,
23
+ options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
24
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element));
25
+
26
+ if(this.mark) Element.remove(this.mark);
27
+ this.mark = document.createElement('div');
28
+ this.mark.appendChild(document.createTextNode(" "));
29
+ document.body.appendChild(this.mark);
30
+ this.mark.style.position = 'absolute';
31
+ this.mark.style.top = options.pointerY + "px";
32
+ this.mark.style.left = options.pointerX + "px";
33
+ this.mark.style.width = "5px";
34
+ this.mark.style.height = "5px;";
35
+ this.mark.style.borderTop = "1px solid red;";
36
+ this.mark.style.borderLeft = "1px solid red;";
37
+
38
+ if(this.step)
39
+ alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
40
+
41
+ $(element).dispatchEvent(oEvent);
42
+ };
43
+
44
+ // Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
45
+ // You need to downgrade to 1.0.4 for now to get this working
46
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
47
+ Event.simulateKey = function(element, eventName) {
48
+ var options = Object.extend({
49
+ ctrlKey: false,
50
+ altKey: false,
51
+ shiftKey: false,
52
+ metaKey: false,
53
+ keyCode: 0,
54
+ charCode: 0
55
+ }, arguments[2] || {});
56
+
57
+ var oEvent = document.createEvent("KeyEvents");
58
+ oEvent.initKeyEvent(eventName, true, true, window,
59
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
60
+ options.keyCode, options.charCode );
61
+ $(element).dispatchEvent(oEvent);
62
+ };
63
+
64
+ Event.simulateKeys = function(element, command) {
65
+ for(var i=0; i<command.length; i++) {
66
+ Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
67
+ }
68
+ };
69
+
70
+ var Test = {};
71
+ Test.Unit = {};
72
+
73
+ // security exception workaround
74
+ Test.Unit.inspect = Object.inspect;
75
+
76
+ Test.Unit.Logger = Class.create();
77
+ Test.Unit.Logger.prototype = {
78
+ initialize: function(log) {
79
+ this.log = $(log);
80
+ if (this.log) {
81
+ this._createLogTable();
82
+ }
83
+ },
84
+ start: function(testName) {
85
+ if (!this.log) return;
86
+ this.testName = testName;
87
+ this.lastLogLine = document.createElement('tr');
88
+ this.statusCell = document.createElement('td');
89
+ this.nameCell = document.createElement('td');
90
+ this.nameCell.className = "nameCell";
91
+ this.nameCell.appendChild(document.createTextNode(testName));
92
+ this.messageCell = document.createElement('td');
93
+ this.lastLogLine.appendChild(this.statusCell);
94
+ this.lastLogLine.appendChild(this.nameCell);
95
+ this.lastLogLine.appendChild(this.messageCell);
96
+ this.loglines.appendChild(this.lastLogLine);
97
+ },
98
+ finish: function(status, summary) {
99
+ if (!this.log) return;
100
+ this.lastLogLine.className = status;
101
+ this.statusCell.innerHTML = status;
102
+ this.messageCell.innerHTML = this._toHTML(summary);
103
+ this.addLinksToResults();
104
+ },
105
+ message: function(message) {
106
+ if (!this.log) return;
107
+ this.messageCell.innerHTML = this._toHTML(message);
108
+ },
109
+ summary: function(summary) {
110
+ if (!this.log) return;
111
+ this.logsummary.innerHTML = this._toHTML(summary);
112
+ },
113
+ _createLogTable: function() {
114
+ this.log.innerHTML =
115
+ '<div id="logsummary"></div>' +
116
+ '<table id="logtable">' +
117
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
118
+ '<tbody id="loglines"></tbody>' +
119
+ '</table>';
120
+ this.logsummary = $('logsummary');
121
+ this.loglines = $('loglines');
122
+ },
123
+ _toHTML: function(txt) {
124
+ return txt.escapeHTML().replace(/\n/g,"<br/>");
125
+ },
126
+ addLinksToResults: function(){
127
+ $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log
128
+ td.title = "Run only this test";
129
+ Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;});
130
+ });
131
+ $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log
132
+ td.title = "Run all tests";
133
+ Event.observe(td, 'click', function(){ window.location.search = "";});
134
+ });
135
+ }
136
+ };
137
+
138
+ Test.Unit.Runner = Class.create();
139
+ Test.Unit.Runner.prototype = {
140
+ initialize: function(testcases) {
141
+ this.options = Object.extend({
142
+ testLog: 'testlog'
143
+ }, arguments[1] || {});
144
+ this.options.resultsURL = this.parseResultsURLQueryParameter();
145
+ this.options.tests = this.parseTestsQueryParameter();
146
+ if (this.options.testLog) {
147
+ this.options.testLog = $(this.options.testLog) || null;
148
+ }
149
+ if(this.options.tests) {
150
+ this.tests = [];
151
+ for(var i = 0; i < this.options.tests.length; i++) {
152
+ if(/^test/.test(this.options.tests[i])) {
153
+ this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
154
+ }
155
+ }
156
+ } else {
157
+ if (this.options.test) {
158
+ this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
159
+ } else {
160
+ this.tests = [];
161
+ for(var testcase in testcases) {
162
+ if(/^test/.test(testcase)) {
163
+ this.tests.push(
164
+ new Test.Unit.Testcase(
165
+ this.options.context ? ' -> ' + this.options.titles[testcase] : testcase,
166
+ testcases[testcase], testcases["setup"], testcases["teardown"]
167
+ ));
168
+ }
169
+ }
170
+ }
171
+ }
172
+ this.currentTest = 0;
173
+ this.logger = new Test.Unit.Logger(this.options.testLog);
174
+ setTimeout(this.runTests.bind(this), 1000);
175
+ },
176
+ parseResultsURLQueryParameter: function() {
177
+ return window.location.search.parseQuery()["resultsURL"];
178
+ },
179
+ parseTestsQueryParameter: function(){
180
+ if (window.location.search.parseQuery()["tests"]){
181
+ return window.location.search.parseQuery()["tests"].split(',');
182
+ };
183
+ },
184
+ // Returns:
185
+ // "ERROR" if there was an error,
186
+ // "FAILURE" if there was a failure, or
187
+ // "SUCCESS" if there was neither
188
+ getResult: function() {
189
+ var hasFailure = false;
190
+ for(var i=0;i<this.tests.length;i++) {
191
+ if (this.tests[i].errors > 0) {
192
+ return "ERROR";
193
+ }
194
+ if (this.tests[i].failures > 0) {
195
+ hasFailure = true;
196
+ }
197
+ }
198
+ if (hasFailure) {
199
+ return "FAILURE";
200
+ } else {
201
+ return "SUCCESS";
202
+ }
203
+ },
204
+ postResults: function() {
205
+ if (this.options.resultsURL) {
206
+ new Ajax.Request(this.options.resultsURL,
207
+ { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
208
+ }
209
+ },
210
+ runTests: function() {
211
+ var test = this.tests[this.currentTest];
212
+ if (!test) {
213
+ // finished!
214
+ this.postResults();
215
+ this.logger.summary(this.summary());
216
+ return;
217
+ }
218
+ if(!test.isWaiting) {
219
+ this.logger.start(test.name);
220
+ }
221
+ test.run();
222
+ if(test.isWaiting) {
223
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
224
+ setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
225
+ } else {
226
+ this.logger.finish(test.status(), test.summary());
227
+ this.currentTest++;
228
+ // tail recursive, hopefully the browser will skip the stackframe
229
+ this.runTests();
230
+ }
231
+ },
232
+ summary: function() {
233
+ var assertions = 0;
234
+ var failures = 0;
235
+ var errors = 0;
236
+ var messages = [];
237
+ for(var i=0;i<this.tests.length;i++) {
238
+ assertions += this.tests[i].assertions;
239
+ failures += this.tests[i].failures;
240
+ errors += this.tests[i].errors;
241
+ }
242
+ return (
243
+ (this.options.context ? this.options.context + ': ': '') +
244
+ this.tests.length + " tests, " +
245
+ assertions + " assertions, " +
246
+ failures + " failures, " +
247
+ errors + " errors");
248
+ }
249
+ };
250
+
251
+ Test.Unit.Assertions = Class.create();
252
+ Test.Unit.Assertions.prototype = {
253
+ initialize: function() {
254
+ this.assertions = 0;
255
+ this.failures = 0;
256
+ this.errors = 0;
257
+ this.messages = [];
258
+ },
259
+ summary: function() {
260
+ return (
261
+ this.assertions + " assertions, " +
262
+ this.failures + " failures, " +
263
+ this.errors + " errors" + "\n" +
264
+ this.messages.join("\n"));
265
+ },
266
+ pass: function() {
267
+ this.assertions++;
268
+ },
269
+ fail: function(message) {
270
+ this.failures++;
271
+ this.messages.push("Failure: " + message);
272
+ },
273
+ info: function(message) {
274
+ this.messages.push("Info: " + message);
275
+ },
276
+ error: function(error) {
277
+ this.errors++;
278
+ this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
279
+ },
280
+ status: function() {
281
+ if (this.failures > 0) return 'failed';
282
+ if (this.errors > 0) return 'error';
283
+ return 'passed';
284
+ },
285
+ assert: function(expression) {
286
+ var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
287
+ try { expression ? this.pass() :
288
+ this.fail(message); }
289
+ catch(e) { this.error(e); }
290
+ },
291
+ assertEqual: function(expected, actual) {
292
+ var message = arguments[2] || "assertEqual";
293
+ try { (expected == actual) ? this.pass() :
294
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
295
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
296
+ catch(e) { this.error(e); }
297
+ },
298
+ assertInspect: function(expected, actual) {
299
+ var message = arguments[2] || "assertInspect";
300
+ try { (expected == actual.inspect()) ? this.pass() :
301
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
302
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
303
+ catch(e) { this.error(e); }
304
+ },
305
+ assertEnumEqual: function(expected, actual) {
306
+ var message = arguments[2] || "assertEnumEqual";
307
+ try { $A(expected).length == $A(actual).length &&
308
+ expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
309
+ this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) +
310
+ ', actual ' + Test.Unit.inspect(actual)); }
311
+ catch(e) { this.error(e); }
312
+ },
313
+ assertNotEqual: function(expected, actual) {
314
+ var message = arguments[2] || "assertNotEqual";
315
+ try { (expected != actual) ? this.pass() :
316
+ this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
317
+ catch(e) { this.error(e); }
318
+ },
319
+ assertIdentical: function(expected, actual) {
320
+ var message = arguments[2] || "assertIdentical";
321
+ try { (expected === actual) ? this.pass() :
322
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
323
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
324
+ catch(e) { this.error(e); }
325
+ },
326
+ assertNotIdentical: function(expected, actual) {
327
+ var message = arguments[2] || "assertNotIdentical";
328
+ try { !(expected === actual) ? this.pass() :
329
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
330
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
331
+ catch(e) { this.error(e); }
332
+ },
333
+ assertNull: function(obj) {
334
+ var message = arguments[1] || 'assertNull';
335
+ try { (obj==null) ? this.pass() :
336
+ this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
337
+ catch(e) { this.error(e); }
338
+ },
339
+ assertMatch: function(expected, actual) {
340
+ var message = arguments[2] || 'assertMatch';
341
+ var regex = new RegExp(expected);
342
+ try { (regex.exec(actual)) ? this.pass() :
343
+ this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
344
+ catch(e) { this.error(e); }
345
+ },
346
+ assertHidden: function(element) {
347
+ var message = arguments[1] || 'assertHidden';
348
+ this.assertEqual("none", element.style.display, message);
349
+ },
350
+ assertNotNull: function(object) {
351
+ var message = arguments[1] || 'assertNotNull';
352
+ this.assert(object != null, message);
353
+ },
354
+ assertType: function(expected, actual) {
355
+ var message = arguments[2] || 'assertType';
356
+ try {
357
+ (actual.constructor == expected) ? this.pass() :
358
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
359
+ '", actual "' + (actual.constructor) + '"'); }
360
+ catch(e) { this.error(e); }
361
+ },
362
+ assertNotOfType: function(expected, actual) {
363
+ var message = arguments[2] || 'assertNotOfType';
364
+ try {
365
+ (actual.constructor != expected) ? this.pass() :
366
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
367
+ '", actual "' + (actual.constructor) + '"'); }
368
+ catch(e) { this.error(e); }
369
+ },
370
+ assertInstanceOf: function(expected, actual) {
371
+ var message = arguments[2] || 'assertInstanceOf';
372
+ try {
373
+ (actual instanceof expected) ? this.pass() :
374
+ this.fail(message + ": object was not an instance of the expected type"); }
375
+ catch(e) { this.error(e); }
376
+ },
377
+ assertNotInstanceOf: function(expected, actual) {
378
+ var message = arguments[2] || 'assertNotInstanceOf';
379
+ try {
380
+ !(actual instanceof expected) ? this.pass() :
381
+ this.fail(message + ": object was an instance of the not expected type"); }
382
+ catch(e) { this.error(e); }
383
+ },
384
+ assertRespondsTo: function(method, obj) {
385
+ var message = arguments[2] || 'assertRespondsTo';
386
+ try {
387
+ (obj[method] && typeof obj[method] == 'function') ? this.pass() :
388
+ this.fail(message + ": object doesn't respond to [" + method + "]"); }
389
+ catch(e) { this.error(e); }
390
+ },
391
+ assertReturnsTrue: function(method, obj) {
392
+ var message = arguments[2] || 'assertReturnsTrue';
393
+ try {
394
+ var m = obj[method];
395
+ if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
396
+ m() ? this.pass() :
397
+ this.fail(message + ": method returned false"); }
398
+ catch(e) { this.error(e); }
399
+ },
400
+ assertReturnsFalse: function(method, obj) {
401
+ var message = arguments[2] || 'assertReturnsFalse';
402
+ try {
403
+ var m = obj[method];
404
+ if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];
405
+ !m() ? this.pass() :
406
+ this.fail(message + ": method returned true"); }
407
+ catch(e) { this.error(e); }
408
+ },
409
+ assertRaise: function(exceptionName, method) {
410
+ var message = arguments[2] || 'assertRaise';
411
+ try {
412
+ method();
413
+ this.fail(message + ": exception expected but none was raised"); }
414
+ catch(e) {
415
+ ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e);
416
+ }
417
+ },
418
+ assertElementsMatch: function() {
419
+ var expressions = $A(arguments), elements = $A(expressions.shift());
420
+ if (elements.length != expressions.length) {
421
+ this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
422
+ return false;
423
+ }
424
+ elements.zip(expressions).all(function(pair, index) {
425
+ var element = $(pair.first()), expression = pair.last();
426
+ if (element.match(expression)) return true;
427
+ this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
428
+ }.bind(this)) && this.pass();
429
+ },
430
+ assertElementMatches: function(element, expression) {
431
+ this.assertElementsMatch([element], expression);
432
+ },
433
+ benchmark: function(operation, iterations) {
434
+ var startAt = new Date();
435
+ (iterations || 1).times(operation);
436
+ var timeTaken = ((new Date())-startAt);
437
+ this.info((arguments[2] || 'Operation') + ' finished ' +
438
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
439
+ return timeTaken;
440
+ },
441
+ _isVisible: function(element) {
442
+ element = $(element);
443
+ if(!element.parentNode) return true;
444
+ this.assertNotNull(element);
445
+ if(element.style && Element.getStyle(element, 'display') == 'none')
446
+ return false;
447
+
448
+ return this._isVisible(element.parentNode);
449
+ },
450
+ assertNotVisible: function(element) {
451
+ this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
452
+ },
453
+ assertVisible: function(element) {
454
+ this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
455
+ },
456
+ benchmark: function(operation, iterations) {
457
+ var startAt = new Date();
458
+ (iterations || 1).times(operation);
459
+ var timeTaken = ((new Date())-startAt);
460
+ this.info((arguments[2] || 'Operation') + ' finished ' +
461
+ iterations + ' iterations in ' + (timeTaken/1000)+'s' );
462
+ return timeTaken;
463
+ }
464
+ };
465
+
466
+ Test.Unit.Testcase = Class.create();
467
+ Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
468
+ initialize: function(name, test, setup, teardown) {
469
+ Test.Unit.Assertions.prototype.initialize.bind(this)();
470
+ this.name = name;
471
+
472
+ if(typeof test == 'string') {
473
+ test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,');
474
+ test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)');
475
+ this.test = function() {
476
+ eval('with(this){'+test+'}');
477
+ }
478
+ } else {
479
+ this.test = test || function() {};
480
+ }
481
+
482
+ this.setup = setup || function() {};
483
+ this.teardown = teardown || function() {};
484
+ this.isWaiting = false;
485
+ this.timeToWait = 1000;
486
+ },
487
+ wait: function(time, nextPart) {
488
+ this.isWaiting = true;
489
+ this.test = nextPart;
490
+ this.timeToWait = time;
491
+ },
492
+ run: function() {
493
+ try {
494
+ try {
495
+ if (!this.isWaiting) this.setup.bind(this)();
496
+ this.isWaiting = false;
497
+ this.test.bind(this)();
498
+ } finally {
499
+ if(!this.isWaiting) {
500
+ this.teardown.bind(this)();
501
+ }
502
+ }
503
+ }
504
+ catch(e) { this.error(e); }
505
+ }
506
+ });
507
+
508
+ // *EXPERIMENTAL* BDD-style testing to please non-technical folk
509
+ // This draws many ideas from RSpec http://rspec.rubyforge.org/
510
+
511
+ Test.setupBDDExtensionMethods = function(){
512
+ var METHODMAP = {
513
+ shouldEqual: 'assertEqual',
514
+ shouldNotEqual: 'assertNotEqual',
515
+ shouldEqualEnum: 'assertEnumEqual',
516
+ shouldBeA: 'assertType',
517
+ shouldNotBeA: 'assertNotOfType',
518
+ shouldBeAn: 'assertType',
519
+ shouldNotBeAn: 'assertNotOfType',
520
+ shouldBeNull: 'assertNull',
521
+ shouldNotBeNull: 'assertNotNull',
522
+
523
+ shouldBe: 'assertReturnsTrue',
524
+ shouldNotBe: 'assertReturnsFalse',
525
+ shouldRespondTo: 'assertRespondsTo'
526
+ };
527
+ var makeAssertion = function(assertion, args, object) {
528
+ this[assertion].apply(this,(args || []).concat([object]));
529
+ };
530
+
531
+ Test.BDDMethods = {};
532
+ $H(METHODMAP).each(function(pair) {
533
+ Test.BDDMethods[pair.key] = function() {
534
+ var args = $A(arguments);
535
+ var scope = args.shift();
536
+ makeAssertion.apply(scope, [pair.value, args, this]); };
537
+ });
538
+
539
+ [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(
540
+ function(p){ Object.extend(p, Test.BDDMethods) }
541
+ );
542
+ };
543
+
544
+ Test.context = function(name, spec, log){
545
+ Test.setupBDDExtensionMethods();
546
+
547
+ var compiledSpec = {};
548
+ var titles = {};
549
+ for(specName in spec) {
550
+ switch(specName){
551
+ case "setup":
552
+ case "teardown":
553
+ compiledSpec[specName] = spec[specName];
554
+ break;
555
+ default:
556
+ var testName = 'test'+specName.gsub(/\s+/,'-').camelize();
557
+ var body = spec[specName].toString().split('\n').slice(1);
558
+ if(/^\{/.test(body[0])) body = body.slice(1);
559
+ body.pop();
560
+ body = body.map(function(statement){
561
+ return statement.strip()
562
+ });
563
+ compiledSpec[testName] = body.join('\n');
564
+ titles[testName] = specName;
565
+ }
566
+ }
567
+ new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });
568
+ };
package.xml ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Mageho_Sortproducts</name>
4
+ <version>1.0.1</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/osl-3.0.php">OSL v3.0 : Open Software License</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Highlight your best products catalog in a few mouse movements with Merchandising Catalog Extension!</summary>
10
+ <description>&lt;h1&gt;English version&lt;/h1&gt;&#xD;
11
+ &#xD;
12
+ &lt;p&gt;Magento extension, merchandising catalog, will help you from your Magento administration to sort your products categories according to their display order.&lt;/p&gt;&#xD;
13
+ &#xD;
14
+ &lt;p&gt;By default, Magento application manually fill the position of products. Tedious and cons-productive when you have more than a dozen products. Here, a miniature product identifies a glance the products you want to highlight. A tooltip, loaded via AJAX, displays information that you want! Simply set the attributes you want to display in the configuration space of back-office (see Photo 3).&lt;/p&gt;&#xD;
15
+ &#xD;
16
+ &lt;p&gt;&lt;span style="font-size:18px"&gt;A real time saver !&lt;/span&gt;&lt;/p&gt;&#xD;
17
+ &#xD;
18
+ &lt;h4&gt;Summary of the highlights of this extension Magento&lt;/h4&gt;&#xD;
19
+ &#xD;
20
+ &lt;ul class="square-list"&gt;&#xD;
21
+ &lt;li&gt;&lt;p&gt;System &lt;em&gt; Drag &amp; Drop &lt;/em&gt; to easily store the products according to the desired position. &lt;/p&gt;&lt;/li&gt;&#xD;
22
+ &lt;li&gt;&lt;p&gt;Display products per photo instantly identifying those desired. &lt;span class="bold"&gt; Ranger hundreds of products in minutes&lt;/p&gt;&lt;/li&gt;&#xD;
23
+ &lt;li&gt;&lt;p&gt;Bubble aid overview to have all the technical characteristics such as price, promotional price, the percentage discount special offer product, reference product ID and all attributes of your choice depending on the configuration of the module.&lt;/p&gt;&#xD;
24
+ &lt;ul class="disc-list"&gt;&#xD;
25
+ &lt;li&gt;&#xD;
26
+ &lt;p&gt;Display with label products not available, it can in the blink of an eye put products exhausted at the end of the list.&lt;/p&gt;&#xD;
27
+ &lt;p&gt;&lt;img src="{{media url="wysiwyg/extension/merchandising/label-product.jpg"}}" width="380" height="160" alt="The products are not available are displayed with the words 'Out of stock'" /&gt;&lt;/p&gt;&#xD;
28
+ &lt;p&gt;It is also possible not to include discontinued products and products whose status is disabled in the list using the module configuration.&lt;/p&gt;&#xD;
29
+ &lt;/li&gt;&#xD;
30
+ &lt;/ul&gt;&#xD;
31
+ &lt;/li&gt;&#xD;
32
+ &lt;/ul&gt;&#xD;
33
+ &#xD;
34
+ &lt;h1&gt;Version fran&#xE7;aise&lt;/h1&gt;&#xD;
35
+ &#xD;
36
+ &lt;p&gt;Cette extension Magento, Merchandising Catalogue, va vous permettre depuis votre administration Magento de trier les produits de vos cat&#xE9;gories selon leur ordre d'affichage.&lt;/p&gt;&#xD;
37
+ &#xD;
38
+ &lt;p&gt;Par d&#xE9;faut, Magento demande de renseigner manuellement la position des produits. Fastidieux et contre-productif quand on a plus d'une dizaine de produits. Ici, une miniature produit permet d'identifier d'un seul coup d'oeil les produits que vous souhaitez mettre en avant. Une bulle d'aide, charg&#xE9;e par AJAX, affiche les informations que l'on souhaitent ! Il suffit de param&#xE9;trer les attributs que l'on souhaite afficher dans l'espace de configuration du back-office (voir Photo 3).&lt;/p&gt;&#xD;
39
+ &#xD;
40
+ &lt;p&gt;&lt;span style="font-size:18px"&gt;Un vrai gain de temps !&lt;/span&gt;&lt;/p&gt;&#xD;
41
+ &#xD;
42
+ &lt;h4&gt;R&#xE9;capitulatif des points forts de cette extension Magento&lt;/h4&gt;&#xD;
43
+ &#xD;
44
+ &lt;ul class="square-list"&gt;&#xD;
45
+ &lt;li&gt;&lt;p&gt;Syst&#xE8;me de &lt;em&gt;Drag &amp; Drop&lt;/em&gt; permettant de ranger ais&#xE9;ment les produits selon la position souhait&#xE9;e.&lt;/p&gt;&lt;/li&gt;&#xD;
46
+ &lt;li&gt;&lt;p&gt;Affichage des produits par photo permettant d'identifier d&#x2019;instantan&#xE9;ment ceux d&#xE9;sir&#xE9;s. &lt;span class="bold"&gt;Ranger des centaines de produits en quelques minutes !&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&#xD;
47
+ &lt;li&gt;&lt;p&gt;Bulle d'aide au survol permettant d'avoir toutes les caract&#xE9;ristiques techniques telles que le Prix, le Prix promotionnel, le Pourcentage de r&#xE9;duction si produit en offre sp&#xE9;ciale, la r&#xE9;f&#xE9;rence, l'ID produit ainsi que tous les attributs de votre choix selon la configuration du module.&lt;/p&gt;&#xD;
48
+ &lt;ul class="disc-list"&gt;&#xD;
49
+ &lt;li&gt;&#xD;
50
+ &lt;p&gt;Affichage avec label des produits non disponibles ; cela permet en un clin d&#x2019;&#x153;il de mettre les produits &#xE9;puis&#xE9;s en fin de liste.&lt;/p&gt;&#xD;
51
+ &lt;p&gt;&lt;img src="{{media url="wysiwyg/extension/merchandising/label-product.jpg"}}" width="380" height="160" alt="Les produits non disponibles sont affich&#xE9;s avec la mention '&#xC9;puis&#xE9;'" /&gt;&lt;/p&gt;&#xD;
52
+ &lt;p&gt;Il est aussi possible de ne pas inclure les produits &#xE9;puis&#xE9;s et les produits dont le statut est d&#xE9;sactiv&#xE9;s dans la liste &#xE0; l'aide de la configuration du module.&lt;/p&gt;&#xD;
53
+ &lt;/li&gt;&#xD;
54
+ &lt;/ul&gt;&#xD;
55
+ &lt;/li&gt;&#xD;
56
+ &lt;/ul&gt;</description>
57
+ <notes>Initial Release</notes>
58
+ <authors><author><name>Ilan PARMENTIER</name><user>Mageho</user><email>ilan.parmentier@mageho.com</email></author></authors>
59
+ <date>2013-05-07</date>
60
+ <time>17:15:18</time>
61
+ <contents><target name="magelocale"><dir name="fr_FR"><file name="Mageho_All.csv" hash="e4f2394435fea990e3f6b1ec968c7273"/><file name="Mageho_Sortproducts.csv" hash="2b75d031a008ee52024ee56281bdf7c3"/></dir></target><target name="mageetc"><dir name="modules"><file name="Mageho_All.xml" hash="92fbf727ce4c64233605ee7276525aed"/><file name="Mageho_Sortproducts.xml" hash="777c7b7f72d78e719eda6c2bbe36b690"/></dir></target><target name="magelocal"><dir name="Mageho"><dir name="All"><dir name="Block"><dir name="System"><dir name="Config"><dir name="Form"><dir name="Fieldset"><file name="Extensions.php" hash="3cb049b292385aa84dff14c1472d41eb"/></dir></dir></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="1f1f8a668f52a26a72df6cfbead6485a"/></dir><dir name="etc"><file name="adminhtml.xml" hash="602adc885b83b88416ab50aced3fc207"/><file name="config.xml" hash="1401e67c17ac1d86daddcdeafca395ef"/><file name="system.xml" hash="3baf3a1ebb1105e7c7260cdcb8a96f9b"/></dir></dir><dir name="Sortproducts"><dir name="Block"><dir name="Adminhtml"><file name="Info.php" hash="d718fada1ab919b5ac6286fda50964bb"/><file name="Info.php.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/><file name="Sortgrid.php" hash="3cf9c9f27e9bbf893356191072303f3c"/><file name="Sortgrid.php.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/><file name="Tab.php" hash="4467d5a3fae44d8f9bf74a6769d2c6a8"/><file name="Tab.php.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/></dir></dir><dir name="Helper"><file name="Data.php" hash="9e24dbfee79b96f5a29692bf132e2ac8"/><file name="Data.php.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/></dir><dir name="Model"><file name="Observer.php" hash="82f727e0ae154da87afceb6738313945"/><file name="Observer.php.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/><dir name="Resource"><file name="Position.php" hash="ad41c330a19fed4ea7945c987d0fcf10"/><file name="Position.php.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/></dir><dir name="System"><dir name="Config"><dir name="Source"><file name="Attributes.php" hash="8e5dd4240d07df4a99fb89578bd4e09a"/><file name="Attributes.php.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/></dir></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><dir name="Mageho"><file name="SortproductsController.php" hash="e0786efd1f2557fed892f8f6a4f67dd4"/><file name="SortproductsController.php.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/></dir></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="732d750e132a184dd0027d49fd4f2de0"/><file name="adminhtml.xml.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/><file name="config.xml" hash="d95c26d38a352fa17b966ad50ace437c"/><file name="config.xml.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/><file name="system.xml" hash="7a3bfbdea4e5cff6e383b50a0e563a35"/><file name="system.xml.LCK" hash="091b90ac6be764824a9fa6f1939caaab"/></dir></dir></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="mageho"><dir name="sortproducts"><file name="placeholder.jpg" hash="b2b682d28a08a748a73d2cda70ab5a57"/><file name="sortproducts.css" hash="0939b00bf328fcb7f6b7f85e61876a3f"/></dir></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="mageho"><file name="sortproducts.xml" hash="e87237697705cb433ea5705656162b75"/></dir></dir><dir name="template"><dir name="mageho"><dir name="sortproducts"><file name="info.phtml" hash="45583e4d8b832206e8c436fe6c41d501"/><file name="sortgrid.phtml" hash="c0b79247a7ac8511cb858aacda6351a9"/><file name="tab.phtml" hash="d55081f3fcbe8ef3d0faba8ec50debe2"/></dir></dir></dir></dir></dir></dir></target><target name="mageweb"><dir name="js"><dir name="mageho"><dir name="sortproducts"><file name="builder.js" hash="c6321f204481f259724bd6455c0fdded"/><file name="controls.js" hash="03b502fd8ae202eb164b348749392720"/><file name="dragdrop.js" hash="046759400db7a6096376e50110104edd"/><file name="effects.js" hash="0dea24894889a4c537e1a451a35f03ca"/><file name="excanvas.js" hash="9d9cbb230cf8d560faf6f6b0f6b5a7ce"/><file name="loading.gif" hash="505aa41462b8840208e1a7d810eb6f48"/><file name="opentip.css" hash="27c49d652671d27a22d91ba32f22ba42"/><file name="opentip.js" hash="5ed35816281e22b7e8b84ba7fdee9485"/><file name="prototype.js" hash="007ae2fe795811f80cedf92fdb591c1b"/><file name="scriptaculous.js" hash="bb679d586e7fccb154156baaef9dfde5"/><file name="slider.js" hash="a9e58cc5f8dd281daa8d8dfee6a78834"/><file name="sound.js" hash="0b22e4379d79b33b696ee3cccf2827b5"/><file name="unittest.js" hash="07c049c24cb838939925425bb555cdf6"/></dir></dir></dir></target></contents>
62
+ <compatible/>
63
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
64
+ </package>
skin/adminhtml/default/default/mageho/sortproducts/placeholder.jpg ADDED
Binary file
skin/adminhtml/default/default/mageho/sortproducts/sortproducts.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ #sortlist-wrapper { overflow:hidden; margin:15px 0; }
2
+ #sortlist li { list-style:none; float:left; margin:4px; padding:4px 4px 2px; border:1px solid #ccc; position:relative; }
3
+ #sortlist li:hover, li.over { border-color:#666; }
4
+
5
+ .product_container { position:relative; }
6
+ .saleable { position:absolute; bottom:0; font-variant:small-caps; font-size:10px; background:#fff; opacity:0.9; padding:0 2px; }