Mageho_Sortproducts - Version 1.0.3

Version Notes

Initial Release

Download this release

Release Info

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


Code changes from version 1.0.2 to 1.0.3

Files changed (32) hide show
  1. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Info.php +65 -0
  2. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Sortgrid.php +116 -0
  3. app/code/local/Mageho/Sortproducts/Block/Adminhtml/Tab.php +28 -0
  4. app/code/local/Mageho/Sortproducts/Helper/Data.php +56 -0
  5. app/code/local/Mageho/Sortproducts/Helper/Data.php.LCK +1 -0
  6. app/code/local/Mageho/Sortproducts/Model/Observer.php +36 -0
  7. app/code/local/Mageho/Sortproducts/Model/Resource/Position.php +43 -0
  8. app/code/local/Mageho/Sortproducts/Model/System/Config/Source/Attributes.php +63 -0
  9. app/code/local/Mageho/Sortproducts/controllers/Adminhtml/Mageho/SortproductsController.php +91 -0
  10. app/code/local/Mageho/Sortproducts/etc/adminhtml.xml +34 -0
  11. app/code/local/Mageho/Sortproducts/etc/config.xml +108 -0
  12. app/code/local/Mageho/Sortproducts/etc/system.xml +70 -0
  13. app/design/adminhtml/default/default/layout/mageho/sortproducts.xml +73 -0
  14. app/design/adminhtml/default/default/template/mageho/sortproducts/info.phtml +37 -0
  15. app/design/adminhtml/default/default/template/mageho/sortproducts/sortgrid.phtml +83 -0
  16. app/design/adminhtml/default/default/template/mageho/sortproducts/tab.phtml +19 -0
  17. app/etc/modules/Mageho_Sortproducts.xml +21 -0
  18. app/locale/fr_FR/Mageho_Sortproducts.csv +14 -0
  19. js/mageho/sortproducts/builder.js +136 -0
  20. js/mageho/sortproducts/controls.js +965 -0
  21. js/mageho/sortproducts/dragdrop.js +974 -0
  22. js/mageho/sortproducts/effects.js +1123 -0
  23. js/mageho/sortproducts/excanvas.js +35 -0
  24. js/mageho/sortproducts/loading.gif +0 -0
  25. js/mageho/sortproducts/opentip.css +460 -0
  26. js/mageho/sortproducts/opentip.js +1285 -0
  27. js/mageho/sortproducts/prototype.js +6081 -0
  28. js/mageho/sortproducts/scriptaculous.js +68 -0
  29. js/mageho/sortproducts/slider.js +275 -0
  30. js/mageho/sortproducts/sound.js +59 -0
  31. js/mageho/sortproducts/unittest.js +568 -0
  32. package.xml +4 -4
app/code/local/Mageho/Sortproducts/Block/Adminhtml/Info.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ public function getSavePercentage($product)
19
+ {
20
+ if ($product->getSpecialPrice()) {
21
+ $save = number_format( (100 - ( $product->getFinalPrice() / $product->getPrice() ) * 100) );
22
+ return '-' . $save . '%';
23
+ }
24
+ return false;
25
+ }
26
+
27
+ public function getAttributesToShow()
28
+ {
29
+ $columnSettings = array();
30
+ $attributes = explode(',', Mage::getStoreConfig('mageho_sortproducts/general/show_attributes'));
31
+ foreach ($attributes as $attribute) {
32
+ $columnSettings[] = trim($attribute);
33
+ }
34
+
35
+ if (empty($columnSettings)) {
36
+ return false;
37
+ }
38
+
39
+ $countColumnSettings = count($columnSettings);
40
+
41
+ if ($countColumnSettings < 1) {
42
+ return false;
43
+ }
44
+ if ($countColumnSettings == 1 && current($columnSettings) == '') {
45
+ return false;
46
+ }
47
+ return $columnSettings;
48
+ }
49
+
50
+ public function getAttributeFrontendLabel($product, $attribute)
51
+ {
52
+ return $product->getResource()->getAttribute($attribute)->getFrontendLabel();
53
+ }
54
+
55
+ public function getAttributeFrontendValue($product, $attribute)
56
+ {
57
+ if ($product->getAttributeText($attribute)) {
58
+ return $product->getAttributeText($attribute);
59
+ } elseif ($product->getData($attribute)) {
60
+ return $product->getData($attribute);
61
+ } else {
62
+ return $this->__('N.C.');
63
+ }
64
+ }
65
+ }
app/code/local/Mageho/Sortproducts/Block/Adminhtml/Sortgrid.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /*
66
+ if ($websiteId = Mage::app()->getWebsite()->getWebsiteId()) {
67
+ $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));
68
+ }
69
+ */
70
+
71
+ $collection->joinField('stock_status', 'cataloginventory/stock_status', 'stock_status', 'product_id=entity_id', array('stock_status' => Mage_CatalogInventory_Model_Stock_Status::STATUS_IN_STOCK));
72
+ }
73
+
74
+ $collection->setOrder('position', 'asc');
75
+
76
+ if ($limit = Mage::helper('mageho_sortproducts')->limitSortProducts()) {
77
+ $collection->setPageSize($limit);
78
+ }
79
+
80
+ $collection->setCurPage(1);
81
+
82
+ $this->_productsCollection = $collection;
83
+ }
84
+ return $this->_productsCollection;
85
+ }
86
+
87
+ public function getImageWidth()
88
+ {
89
+ return Mage::helper('mageho_sortproducts')->getImageWidth();
90
+ }
91
+
92
+ /**
93
+ * Retrieve Session Form Key
94
+ *
95
+ * @return string
96
+ */
97
+ public function getFormKey()
98
+ {
99
+ return Mage::getSingleton('core/session')->getFormKey();
100
+ }
101
+
102
+ public function getSaveUrl()
103
+ {
104
+ return $this->getUrl('*/*/save', array('_current' => true));
105
+ }
106
+
107
+ public function getProductInfoUrl($product)
108
+ {
109
+ return $this->getUrl('*/*/info', array('id' => $product->getId(), '_current' => true));
110
+ }
111
+
112
+ public function isDisabled($product)
113
+ {
114
+ return $product->getStatus() && ($product->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_DISABLED);
115
+ }
116
+ }
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/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/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
+
12
+ class Mageho_Sortproducts_Model_Resource_Position extends Mage_Catalog_Model_Resource_Category
13
+ {
14
+ /**
15
+ * Initialize resource
16
+ */
17
+ public function __construct()
18
+ {
19
+ parent::__construct();
20
+ $resource = Mage::getSingleton('core/resource');
21
+ $this->setType('catalog_category')
22
+ ->setConnection(
23
+ $resource->getConnection('catalog_read'),
24
+ $resource->getConnection('catalog_write')
25
+ );
26
+
27
+ $this->_productCategoryTable = $resource->getTableName('catalog/category_product');
28
+ $this->_productCategoryIndexTable = $resource->getTableName('catalog/category_product_index');
29
+ }
30
+
31
+ public function save($position, $productId, $categoryId)
32
+ {
33
+ $condition = array(
34
+ $this->_getWriteAdapter()->quoteInto("product_id=?", $productId),
35
+ $this->_getWriteAdapter()->quoteInto("category_id=?", $categoryId)
36
+ );
37
+
38
+ $db = $this->_getWriteAdapter();
39
+ $db->update($this->_productCategoryTable, array('position' => $position), $condition);
40
+ $db->update($this->_productCategoryIndexTable, array('position' => $position), $condition);
41
+ return $this;
42
+ }
43
+ }
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' => utf8_decode(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/controllers/Adminhtml/Mageho/SortproductsController.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 extends Mage_Adminhtml_Controller_Action
11
+ {
12
+ public function indexAction()
13
+ {
14
+ $this->loadLayout();
15
+ $this->renderLayout();
16
+ }
17
+
18
+ public function tabAction()
19
+ {
20
+ $this->getResponse()->setBody(
21
+ $this->getLayout()->createBlock('mageho_sortproducts/adminhtml_tab')->toHtml()
22
+ );
23
+ }
24
+
25
+ public function infoAction()
26
+ {
27
+ $productId = $this->getRequest()->getParam('id', false);
28
+ $product = Mage::getModel('catalog/product')
29
+ ->setStoreId($this->getRequest()->getParam('store', 0));
30
+
31
+ if ($productId) {
32
+ $product->load($productId);
33
+
34
+ Mage::register('product', $product);
35
+ Mage::register('current_product', $product);
36
+
37
+ $this->getResponse()->setBody(
38
+ $this->getLayout()->createBlock('mageho_sortproducts/adminhtml_info')->toHtml()
39
+ );
40
+ }
41
+ }
42
+
43
+ public function saveAction()
44
+ {
45
+ $data = $this->getRequest()->getPost('data');
46
+ $categoryId = $this->getRequest()->getParam('id', false);
47
+
48
+ try {
49
+ if (! $data) {
50
+ throw new Exception('no data post parameters');
51
+ }
52
+
53
+ if ($categoryId) {
54
+ $category = Mage::getModel('catalog/category')
55
+ ->setStoreId($this->getRequest()->getParam('store', 0))
56
+ ->load($categoryId);
57
+
58
+ if ($category->getId()) {
59
+ parse_str($data);
60
+ for ($i = 0; $i < count($sortlist); $i++) {
61
+ $position = $i;
62
+ $productId = $sortlist[$i];
63
+
64
+ Mage::getResourceModel('mageho_sortproducts/position')->save($position, $productId, $category->getId());
65
+ }
66
+
67
+ Mage::app()->cleanCache(array(Mage_Catalog_Model_Category::CACHE_TAG.'_'.$category->getId()));
68
+
69
+ Mage::getSingleton('adminhtml/session')->addSuccess(
70
+ Mage::helper('mageho_sortproducts')->__('The position of products has been saved with success.')
71
+ );
72
+ }
73
+ }
74
+ } catch (Exception $e) {
75
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
76
+ }
77
+
78
+ $resetUrl = Mage::helper('adminhtml')->getUrl('adminhtml/catalog_category/edit', array('active_tab_id' => 'category_info_tabs_sortproducts', '_current' => true));
79
+ $this->getResponse()->setBody("window.top.categoryReset('{$resetUrl}', true);");
80
+ }
81
+
82
+ /**
83
+ * Check if admin has permissions to visit categories pages
84
+ *
85
+ * @return boolean
86
+ */
87
+ protected function _isAllowed()
88
+ {
89
+ return Mage::getSingleton('admin/session')->isAllowed('catalog/categories');
90
+ }
91
+ }
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/config.xml ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /*
4
+ * @category Mageho
5
+ * @package Mageho_Sortproducts
6
+ * @version 1.0.0
7
+ * @copyright Copyright (c) 2013 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>mageho_sortproducts_resource</resourceModel>
32
+ </mageho_sortproducts>
33
+ <mageho_sortproducts_resource>
34
+ <class>Mageho_Sortproducts_Model_Resource</class>
35
+ </mageho_sortproducts_resource>
36
+ </models>
37
+ <resources>
38
+ <mageho_sortproducts_setup>
39
+ <setup>
40
+ <module>Mageho_Sortproducts</module>
41
+ </setup>
42
+ <connection>
43
+ <use>core_setup</use>
44
+ </connection>
45
+ </mageho_sortproducts_setup>
46
+ <mageho_sortproducts_write>
47
+ <connection>
48
+ <use>core_write</use>
49
+ </connection>
50
+ </mageho_sortproducts_write>
51
+ <mageho_sortproducts_read>
52
+ <connection>
53
+ <use>core_read</use>
54
+ </connection>
55
+ </mageho_sortproducts_read>
56
+ </resources>
57
+ <events>
58
+ <adminhtml_catalog_category_tabs>
59
+ <observers>
60
+ <mageho_sortproducts>
61
+ <type>singleton</type>
62
+ <class>mageho_sortproducts/observer</class>
63
+ <method>addTabToCategoryPage</method>
64
+ </mageho_sortproducts>
65
+ </observers>
66
+ </adminhtml_catalog_category_tabs>
67
+ </events>
68
+ </global>
69
+ <admin>
70
+ <routers>
71
+ <adminhtml>
72
+ <args>
73
+ <modules>
74
+ <Mageho_Sortproducts before="Mage_Adminhtml">Mageho_Sortproducts_Adminhtml</Mageho_Sortproducts>
75
+ </modules>
76
+ </args>
77
+ </adminhtml>
78
+ </routers>
79
+ </admin>
80
+ <adminhtml>
81
+ <translate>
82
+ <modules>
83
+ <Mageho_Sortproducts>
84
+ <files>
85
+ <default>Mageho_Sortproducts.csv</default>
86
+ </files>
87
+ </Mageho_Sortproducts>
88
+ </modules>
89
+ </translate>
90
+ <layout>
91
+ <updates>
92
+ <mageho_sortproducts>
93
+ <file>mageho/sortproducts.xml</file>
94
+ </mageho_sortproducts>
95
+ </updates>
96
+ </layout>
97
+ </adminhtml>
98
+ <default>
99
+ <mageho_sortproducts>
100
+ <general>
101
+ <limit_products>0</limit_products>
102
+ <image_width>50</image_width>
103
+ <display_only_enabled_products>0</display_only_enabled_products>
104
+ <display_out_stock_products>0</display_out_stock_products>
105
+ </general>
106
+ </mageho_sortproducts>
107
+ </default>
108
+ </config>
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>catalog</tab>
16
+ <frontend_type>text</frontend_type>
17
+ <sort_order>45</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/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 $this->getAttributeFrontendValue($_product, $attribute); ?></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_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_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 de 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 CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Mageho_Sortproducts</name>
4
- <version>1.0.2</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>
@@ -56,9 +56,9 @@
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-19</date>
60
- <time>20:44:25</time>
61
- <contents><target name="magelocale"><dir name="fr_FR"><file name="Mageho_Sortproducts.csv" hash=""/></dir></target><target name="mageetc"><dir name="modules"><file name="Mageho_Sortproducts.xml" hash=""/></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=""/></dir></dir></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>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Mageho_Sortproducts</name>
4
+ <version>1.0.3</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>
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-31</date>
60
+ <time>23:28:59</time>
61
+ <contents><target name="magelocale"><dir name="fr_FR"><file name="Mageho_Sortproducts.csv" hash="9b45622871c0b6ae37a7f900d2b3783a"/></dir></target><target name="mageetc"><dir name="modules"><file name="Mageho_Sortproducts.xml" hash="777c7b7f72d78e719eda6c2bbe36b690"/></dir></target><target name="magelocal"><dir name="Mageho"><dir name="Sortproducts"><dir name="Block"><dir name="Adminhtml"><file name="Info.php" hash="07f15e9285e11113d1562fca81de3001"/><file name="Sortgrid.php" hash="28867799eb7102ed03f9d2ad7568c010"/><file name="Tab.php" hash="4467d5a3fae44d8f9bf74a6769d2c6a8"/></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"/><dir name="Resource"><file name="Position.php" hash="c47463a6aeb6307507097e67c177b978"/></dir><dir name="System"><dir name="Config"><dir name="Source"><file name="Attributes.php" hash="c744040d3855d9c821371f0daabdee6f"/></dir></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><dir name="Mageho"><file name="SortproductsController.php" hash="3d82d570d8623fb0749866370d4a1204"/></dir></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="732d750e132a184dd0027d49fd4f2de0"/><file name="config.xml" hash="de8c53618ffdcd23c6cc998ca2abc55a"/><file name="system.xml" hash="b764a23c8c71c1ac8a586df5e96fb206"/></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="f03b0609338ad1b69bd90efe64bf4c02"/><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>