Mana_Filters - Version 12.04.21.23

Version Notes

Adds multiple selection features for attribute and price filters in Magento layered navigation.

Download this release

Release Info

Developer Magento Core Team
Extension Mana_Filters
Version 12.04.21.23
Comparing to
See all releases


Code changes from version 1.1.0 to 12.04.21.23

Files changed (132) hide show
  1. app/code/local/Mana/Core/Block/Js.php +70 -0
  2. app/code/local/Mana/Core/Block/List.php +41 -0
  3. app/code/local/Mana/Core/Block/Singleton.php +24 -0
  4. app/code/local/Mana/Core/Helper/Data.php +416 -0
  5. app/code/local/Mana/Core/Helper/Files.php +75 -0
  6. app/code/local/Mana/Core/Helper/Js.php +48 -0
  7. app/code/local/Mana/Core/Helper/Lock.php +102 -0
  8. app/code/local/Mana/Core/Model/Attribute/Scope.php +22 -0
  9. app/code/local/Mana/Core/Model/Callback.php +40 -0
  10. app/code/local/Mana/Core/Model/Config/Default.php +21 -0
  11. app/code/local/Mana/Core/Model/Eav.php +49 -0
  12. app/code/local/Mana/Core/Model/Html/Filter.php +46 -0
  13. app/code/local/Mana/Core/Model/Html/Parser.php +179 -0
  14. app/code/local/Mana/Core/Model/Html/Reader.php +319 -0
  15. app/code/local/Mana/Core/Model/Html/State.php +35 -0
  16. app/code/local/Mana/Core/Model/Html/Token.php +80 -0
  17. app/code/local/Mana/Core/Model/Object.php +167 -0
  18. app/code/local/Mana/Core/Model/Observer.php +161 -0
  19. app/code/local/Mana/Core/Model/Source/Abstract.php +66 -0
  20. app/code/local/Mana/Core/Model/Source/Config.php +30 -0
  21. app/code/local/Mana/Core/Model/Source/Country.php +16 -0
  22. app/code/local/Mana/Core/Model/Source/Yesno.php +21 -0
  23. app/code/local/Mana/Core/Profiler.php +33 -0
  24. app/code/local/Mana/Core/Resource/Attribute/Collection.php +51 -0
  25. app/code/local/Mana/Core/Resource/Eav.php +93 -0
  26. app/code/local/Mana/Core/Resource/Eav/Collection.php +213 -0
  27. app/code/local/Mana/Core/Resource/Eav/Collection/Derived.php +132 -0
  28. app/code/local/Mana/Core/Resource/Eav/Setup.php +166 -0
  29. app/code/local/Mana/Core/Resource/Virtual/Collection.php +121 -0
  30. app/code/local/Mana/Core/etc/config.xml +177 -0
  31. app/code/local/Mana/Core/sql/mana_core_setup/mysql4-install-1.1.1.php +36 -0
  32. app/code/local/Mana/Db/Exception/Validation.php +11 -0
  33. app/code/local/Mana/Db/Helper/Data.php +341 -0
  34. app/code/local/Mana/Db/Model/Indexer.php +43 -0
  35. app/code/local/Mana/Db/Model/Object.php +76 -0
  36. app/code/local/Mana/Db/Model/Observer.php +113 -0
  37. app/code/local/Mana/Db/Model/Replication/Target.php +69 -0
  38. app/code/local/Mana/Db/Model/Validation.php +27 -0
  39. app/code/local/Mana/Db/Model/Virtual/Result.php +27 -0
  40. app/code/local/Mana/Db/Resource/Edit/Session.php +48 -0
  41. app/code/local/Mana/Db/Resource/Object.php +211 -0
  42. app/code/local/Mana/Db/Resource/Object/Collection.php +96 -0
  43. app/code/local/Mana/Db/Resource/Replicate.php +30 -0
  44. app/code/local/Mana/Db/etc/config.xml +183 -0
  45. app/code/local/Mana/Db/sql/mana_db_setup/mysql4-install-11.09.28.09.php +35 -0
  46. app/code/local/Mana/Db/sql/mana_db_setup/mysql4-upgrade-11.09.28.09-11.09.28.10.php +14 -0
  47. app/code/local/Mana/Db/sql/mana_db_setup/mysql4-upgrade-11.10.08.23-11.10.20.22.php +27 -0
  48. app/code/local/Mana/Filters/Block/Filter.php +59 -0
  49. app/code/local/Mana/Filters/Block/Filter/Attribute.php +0 -42
  50. app/code/local/Mana/Filters/Block/Filter/Attribute/Search.php +0 -19
  51. app/code/local/Mana/Filters/Block/Filter/Category.php +0 -41
  52. app/code/local/Mana/Filters/Block/Filter/Decimal.php +0 -23
  53. app/code/local/Mana/Filters/Block/Filter/Price.php +0 -41
  54. app/code/local/Mana/Filters/Block/Layer.php +20 -0
  55. app/code/local/Mana/Filters/Block/Search.php +59 -0
  56. app/code/local/Mana/Filters/Block/State.php +28 -0
  57. app/code/local/Mana/Filters/Block/View.php +59 -0
  58. app/code/local/Mana/Filters/Block/View/Category.php +0 -75
  59. app/code/local/Mana/Filters/Block/View/Search.php +0 -30
  60. app/code/local/Mana/Filters/Helper/Data.php +132 -0
  61. app/code/local/Mana/Filters/Helper/Extended.php +1 -70
  62. app/code/local/Mana/Filters/Model/Config/Display/Default.php +18 -0
  63. app/code/local/Mana/Filters/Model/Filter.php +56 -0
  64. app/code/local/Mana/Filters/Model/Filter/Attribute.php +35 -4
  65. app/code/local/Mana/Filters/Model/Filter/Attribute/Search.php +0 -19
  66. app/code/local/Mana/Filters/Model/Filter/Category.php +67 -0
  67. app/code/local/Mana/Filters/Model/Filter/Decimal.php +244 -0
  68. app/code/local/Mana/Filters/Model/Filter/Default.php +53 -0
  69. app/code/local/Mana/Filters/Model/Filter/Price.php +108 -25
  70. app/code/local/Mana/Filters/Model/Filter2.php +59 -0
  71. app/code/local/Mana/Filters/Model/Filter2/Store.php +38 -0
  72. app/code/local/Mana/Filters/Model/Filter2/Value.php +22 -0
  73. app/code/local/Mana/Filters/Model/Filter2/Value/Store.php +22 -0
  74. app/code/local/Mana/Filters/Model/Item.php +43 -6
  75. app/code/local/Mana/Filters/Model/Observer.php +48 -0
  76. app/code/local/Mana/Filters/Model/Operation.php +19 -0
  77. app/code/local/Mana/Filters/Model/Sort.php +55 -0
  78. app/code/local/Mana/Filters/Model/Source/Display.php +33 -0
  79. app/code/local/Mana/Filters/Model/Source/Display/All.php +43 -0
  80. app/code/local/Mana/Filters/Model/Source/Display/Attribute.php +16 -0
  81. app/code/local/Mana/Filters/Model/Source/Display/Category.php +16 -0
  82. app/code/local/Mana/Filters/Model/Source/Display/Decimal.php +16 -0
  83. app/code/local/Mana/Filters/Model/Source/Display/Price.php +16 -0
  84. app/code/local/Mana/Filters/Model/Source/Filterable.php +28 -0
  85. app/code/local/Mana/Filters/Resource/Filter.php +25 -0
  86. app/code/local/Mana/Filters/Resource/Filter/Attribute.php +38 -17
  87. app/code/local/Mana/Filters/Resource/Filter/Attribute/Collection.php +19 -0
  88. app/code/local/Mana/Filters/Resource/Filter/Collection.php +48 -0
  89. app/code/local/Mana/Filters/Resource/Filter/Collection/Derived.php +66 -0
  90. app/code/local/Mana/Filters/Resource/Filter/Decimal.php +135 -0
  91. app/code/local/Mana/Filters/Resource/Filter/Price.php +108 -32
  92. app/code/local/Mana/Filters/Resource/Filter2.php +279 -0
  93. app/code/local/Mana/Filters/Resource/Filter2/Collection.php +30 -0
  94. app/code/local/Mana/Filters/Resource/Filter2/Store.php +203 -0
  95. app/code/local/Mana/Filters/Resource/Filter2/Store/Collection.php +59 -0
  96. app/code/local/Mana/Filters/Resource/Filter2/Value.php +145 -0
  97. app/code/local/Mana/Filters/Resource/Filter2/Value/Collection.php +25 -0
  98. app/code/local/Mana/Filters/Resource/Filter2/Value/Store.php +171 -0
  99. app/code/local/Mana/Filters/Resource/Filter2/Value/Store/Collection.php +26 -0
  100. app/code/local/Mana/Filters/Resource/Setup.php +134 -0
  101. app/code/local/Mana/Filters/etc/config.xml +112 -11
  102. app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-install-1.1.1.php +32 -0
  103. app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-1.1.1-1.9.1.php +18 -0
  104. app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-11.09.24.09-11.09.28.09.php +69 -0
  105. app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-11.10.19.18-11.10.23.01.php +101 -0
  106. app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-12.01.14.09-12.01.15.14.php +32 -0
  107. app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-12.04.10.23-12.04.17.18.php +32 -0
  108. app/design/adminhtml/default/default/layout/mana_core.xml +61 -0
  109. app/design/adminhtml/default/default/template/mana/core/js.phtml +21 -0
  110. app/design/adminhtml/default/default/template/mana/core/wait.phtml +13 -0
  111. app/design/frontend/base/default/layout/mana_core.xml +67 -0
  112. app/design/frontend/base/default/layout/mana_filters.xml +10 -2
  113. app/design/frontend/base/default/template/mana/core/js.phtml +21 -0
  114. app/design/frontend/base/default/template/mana/core/popup.phtml +12 -0
  115. app/design/frontend/base/default/template/mana/core/wait.phtml +13 -0
  116. app/design/frontend/base/default/template/mana/filters/items/list.phtml +4 -5
  117. app/design/frontend/base/default/template/mana/filters/items/list_popup.phtml +49 -0
  118. app/design/frontend/base/default/template/mana/filters/items/standard.phtml +34 -0
  119. app/design/frontend/base/default/template/mana/filters/items/standard_popup.phtml +43 -0
  120. app/design/frontend/base/default/template/mana/filters/state.phtml +36 -0
  121. app/etc/modules/Mana_Core.xml +21 -0
  122. app/etc/modules/Mana_Db.xml +23 -0
  123. app/etc/modules/Mana_Filters.xml +2 -0
  124. app/locale/en_US/Mana_Core.csv +19 -0
  125. app/locale/en_US/Mana_Db.csv +14 -0
  126. app/locale/en_US/Mana_Filters.csv +20 -1
  127. js/jquery/advListRotator.js +591 -0
  128. js/jquery/fileuploader.js +1257 -0
  129. js/jquery/history.adapter.jquery.js +58 -0
  130. js/jquery/history.js +1866 -0
  131. js/jquery/jquery-ui.js +11827 -0
  132. js/jquery/jquery-ui.min.js +246 -0
app/code/local/Mana/Core/Block/Js.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * All visual blocks on page use this block to provide initial data and translations to client scripts
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Block_Js extends Mage_Core_Block_Template {
15
+ /**
16
+ * Contains all the translations registered to be passed to client-side scripts
17
+ * @var array | null
18
+ */
19
+ protected $_translations;
20
+ /**
21
+ * Contains all key-value pair arrays registered to be passed to client-side scripts
22
+ * @var array | null
23
+ */
24
+ protected $_options;
25
+
26
+ /**
27
+ * Makes translations of specified strings to be available in client-side scripts.
28
+ * @param array $translations
29
+ * @return Mana_Core_Helper_Js
30
+ */
31
+ public function translations($translations) {
32
+ foreach ($translations as $key) {
33
+ $value = $this->__($key);
34
+ //if ($key != $value) {
35
+ if (!$this->_translations) $this->_translations = array();
36
+ $this->_translations[$key] = $value;
37
+ //}
38
+ }
39
+ return $this;
40
+ }
41
+ /**
42
+ * Makes options (specified in $options key-value pair array) for HTML element (selected with $selector)
43
+ * to be available in client-side scripts.
44
+ * @param string $selector
45
+ * @param array $options
46
+ * @return Mana_Core_Helper_Js
47
+ */
48
+ public function options($selector, $options) {
49
+ if (!$this->_options) $this->_options = array();
50
+ if (!isset($this->_options[$selector])) {
51
+ $this->_options[$selector] = $options;
52
+ }
53
+ else {
54
+ foreach ($options as $key => $value) {
55
+ $this->_options[$selector][$key] = $value;
56
+ }
57
+ }
58
+ return $this;
59
+ }
60
+ /**
61
+ * Returns all the translations registered to be passed to client-side scripts
62
+ * @var array | null
63
+ */
64
+ public function getTranslations() { return $this->_translations; }
65
+ /**
66
+ * Returns all key-value pair arrays registered to be passed to client-side scripts
67
+ * @var array | null
68
+ */
69
+ public function getOptions() { return $this->_options; }
70
+ }
app/code/local/Mana/Core/Block/List.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Block_List extends Mage_Core_Block_Text_List {
15
+ protected function _toHtml() {
16
+ if ($this->_visible) {
17
+ return parent::_toHtml();
18
+ }
19
+ else {
20
+ return '';
21
+ }
22
+ }
23
+
24
+ protected $_visible = true;
25
+ public function showIfFlagSet($param) {
26
+ $this->_visible = Mage::getStoreConfigFlag($param);
27
+ return $this;
28
+ }
29
+ public function showIfFlagNotSet($param) {
30
+ $this->_visible = ! Mage::getStoreConfigFlag($param);
31
+ return $this;
32
+ }
33
+ public function showIfValueEquals($param, $value) {
34
+ $this->_visible = Mage::getStoreConfig($param) == $value;
35
+ return $this;
36
+ }
37
+ public function showIfValueNotEquals($param, $value) {
38
+ $this->_visible = Mage::getStoreConfig($param) != $value;
39
+ return $this;
40
+ }
41
+ }
app/code/local/Mana/Core/Block/Singleton.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Core_Block_Singleton extends Mage_Core_Block_Text_List {
13
+ protected $_singletons = array();
14
+ public function addSingletonBlock($type, $name, $template = null) {
15
+ if (!isset($this->_singletons[$name])) {
16
+ $this->_singletons[$name] = $block = $this->getLayout()->createBlock($type, $name);
17
+ if ($template) {
18
+ $block->setTemplate($template);
19
+ }
20
+ $this->append($block, $name);
21
+ }
22
+ return $this;
23
+ }
24
+ }
app/code/local/Mana/Core/Helper/Data.php ADDED
@@ -0,0 +1,416 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* BASED ON SNIPPET: New Module/Helper/Data.php */
9
+ /**
10
+ * Generic helper functions for Mana_Core module. This class is a must for any module even if empty.
11
+ * @author Mana Team
12
+ */
13
+ class Mana_Core_Helper_Data extends Mage_Core_Helper_Abstract {
14
+ /**
15
+ * Retrieve config value for store by path. By default uses standard Magento function to query core_config_data
16
+ * table and use config.xml for default value. Though this could be replaced by extensions (in later versions).
17
+ *
18
+ * @param string $path
19
+ * @param mixed $store
20
+ * @return mixed
21
+ */
22
+ public function getStoreConfig($path, $store = null) {
23
+ return Mage::getStoreConfig($path, $store);
24
+ }
25
+ public function endsWith($haystack, $needle) {
26
+ return (strrpos($haystack, $needle) === strlen($haystack) - strlen($needle));
27
+ }
28
+ public function startsWith($haystack, $needle) {
29
+ return (strpos($haystack, $needle) === 0);
30
+ }
31
+ public static $upperCaseCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
32
+ public static $lowerCaseCharacters = 'abcdefghijklmnopqrstuvwxyz';
33
+ public static $whitespaceCharacters = " \t\r\n";
34
+ protected function _explodeIdentifier($identifier) {
35
+ $result = array();
36
+ $segment = substr($identifier, 0, 1);
37
+ $mode = 0; // not recognized
38
+ if ($segment == '_') { $mode = 1; $result[] = ''; $segment = ''; }
39
+ elseif ($segment == '-') { $mode = 3; $result[] = ''; $segment = ''; }
40
+ $allUppers = !$segment || strpos(self::$upperCaseCharacters, $segment) !== false;
41
+ for ($i = 1; $i < strlen($identifier); $i++) {
42
+ $ch = substr($identifier, $i, 1);
43
+ switch ($mode) {
44
+ case 0: // not recognized
45
+ if ($ch == '_') {
46
+ $mode = 1; // underscored
47
+ $result[] = $segment;
48
+ $segment = '';
49
+ }
50
+ elseif ($ch == '-') {
51
+ $mode = 3; // hyphened
52
+ $result[] = $segment;
53
+ $segment = '';
54
+ }
55
+ elseif (strpos(self::$upperCaseCharacters, $ch) !== false) {
56
+ if (!$allUppers) {
57
+ $mode = 2; // case separated
58
+ $result[] = $segment;
59
+ $segment = '';
60
+ }
61
+ $segment .= $ch;
62
+ }
63
+ else {
64
+ if (strpos(self::$lowerCaseCharacters, $ch) !== false) $allUppers = false;
65
+ $segment .= $ch;
66
+ }
67
+ break;
68
+ case 1: // underscored
69
+ if ($ch == '_') {
70
+ $result[] = $segment;
71
+ $segment = '';
72
+ }
73
+ else {
74
+ $segment .= $ch;
75
+ }
76
+ break;
77
+ case 2: // case separated
78
+ if (strpos(self::$upperCaseCharacters, $ch) !== false) {
79
+ $result[] = $segment;
80
+ $segment = '';
81
+ }
82
+ $segment .= $ch;
83
+ break;
84
+ case 3: // hyphened
85
+ if ($ch == '-') {
86
+ $result[] = $segment;
87
+ $segment = '';
88
+ }
89
+ else {
90
+ $segment .= $ch;
91
+ }
92
+ break;
93
+ default:
94
+ throw new Exception('Not implemented.');
95
+ }
96
+ }
97
+ if ($segment) $result[] = $segment;
98
+ return $result;
99
+ }
100
+ public function pascalCased($identifier) {
101
+ $result = '';
102
+ foreach (self::_explodeIdentifier($identifier) as $segment) {
103
+ $result .= ucfirst(strtolower($segment));
104
+ }
105
+ return $result;
106
+ }
107
+ public function camelCased($identifier) {
108
+ $result = '';
109
+ $first = true;
110
+ foreach (self::_explodeIdentifier($identifier) as $segment) {
111
+ if ($first) {
112
+ $result .= strtolower($segment);
113
+ $first = false;
114
+ }
115
+ else {
116
+ $result .= ucfirst(strtolower($segment));
117
+ }
118
+ }
119
+ return $result;
120
+ }
121
+ public function lowerCased($identifier) {
122
+ $result = '';
123
+ $separatorNeeded = false;
124
+ foreach (self::_explodeIdentifier($identifier) as $segment) {
125
+ if ($separatorNeeded) $result .= '_'; else $separatorNeeded = true;
126
+ $result .= strtolower($segment);
127
+ }
128
+ return $result;
129
+ }
130
+ public function upperCased($identifier) {
131
+ $result = '';
132
+ $separatorNeeded = false;
133
+ foreach (self::_explodeIdentifier($identifier) as $segment) {
134
+ if ($separatorNeeded) $result .= '_'; else $separatorNeeded = true;
135
+ $result .= strtoupper($segment);
136
+ }
137
+ return $result;
138
+ }
139
+ public function hyphenCased($identifier) {
140
+ $result = '';
141
+ $separatorNeeded = false;
142
+ foreach (self::_explodeIdentifier($identifier) as $segment) {
143
+ if ($separatorNeeded) $result .= '-'; else $separatorNeeded = true;
144
+ $result .= strtolower($segment);
145
+ }
146
+ return $result;
147
+ }
148
+ protected $_urlSymbols;
149
+ protected function _initUrlSymbols() {
150
+ if (!$this->_urlSymbols) {
151
+ $this->_urlSymbols = array();
152
+ $this->_urlSymbols['-'] = Mage::getStoreConfig('mana_filters/seo/dash');
153
+ $this->_urlSymbols['/'] = Mage::getStoreConfig('mana_filters/seo/slash');
154
+ $this->_urlSymbols['+'] = Mage::getStoreConfig('mana_filters/seo/plus');
155
+ $this->_urlSymbols['_'] = Mage::getStoreConfig('mana_filters/seo/underscore');
156
+ $this->_urlSymbols["'"] = Mage::getStoreConfig('mana_filters/seo/quote');
157
+ $this->_urlSymbols['"'] = Mage::getStoreConfig('mana_filters/seo/double_quote');
158
+ $this->_urlSymbols['%'] = Mage::getStoreConfig('mana_filters/seo/percent');
159
+ $this->_urlSymbols['#'] = Mage::getStoreConfig('mana_filters/seo/hash');
160
+ $this->_urlSymbols['&'] = Mage::getStoreConfig('mana_filters/seo/ampersand');
161
+ $this->_urlSymbols[' '] = Mage::getStoreConfig('mana_filters/seo/space');
162
+ }
163
+ return $this;
164
+ }
165
+ public function labelToUrl($text) {
166
+ $this->_initUrlSymbols();
167
+ foreach ($this->_urlSymbols as $symbol => $urlSymbol) {
168
+ $text = str_replace($symbol, $urlSymbol, $text);
169
+ }
170
+ return $text;
171
+ }
172
+ public function urlToLabel($text) {
173
+ $this->_initUrlSymbols();
174
+ $result = '';
175
+ for ($i = 0; $i < mb_strlen($text); ) {
176
+ $found = false;
177
+ foreach ($this->_urlSymbols as $symbol => $urlSymbol) {
178
+ if (mb_strpos($text, $urlSymbol, $i) === $i) {
179
+ $result .= $symbol;
180
+ $i += mb_strlen($urlSymbol);
181
+ $found = true;
182
+ break;
183
+ }
184
+ }
185
+ if (!$found) {
186
+ $result .= mb_substr($text, $i++, 1);
187
+ }
188
+ }
189
+ return $result;
190
+ }
191
+ public function translateConfig($config) {
192
+ $this->_translateConfigRecursively($config);
193
+ return $config;
194
+ }
195
+ /**
196
+ * Enter description here ...
197
+ * @param Varien_Simplexml_Element $config
198
+ * @param array | null $fields
199
+ * @param string | null $module
200
+ */
201
+ protected function _translateConfigRecursively($config, $fields = null, $module = null) {
202
+ if ($fields && in_array($config->getName(), $fields)) {
203
+ $name = $config->getName();
204
+ $parent = $config->getParent();
205
+ $value = (string)$config;
206
+ $moduleName = $module ? $module : $this->_getModuleName();
207
+ $parent->$name = Mage::app()->getTranslator()->translate(array(new Mage_Core_Model_Translate_Expr($value, $moduleName)));
208
+ }
209
+ $fields = isset($config['translate']) ? explode(',', (string)$config['translate']) : null;
210
+ $module = isset($config['module']) ? (string)$config['module'] : null;
211
+ foreach ($config->children() as $key => $value) {
212
+ $this->_translateConfigRecursively($value, $fields, $module);
213
+ }
214
+ }
215
+ public function mergeConfig($mergeToObject, $extensions) {
216
+ foreach ($extensions as $extension) {
217
+ if ($extension) {
218
+ $mergeModel = new Mage_Core_Model_Config_Base;
219
+ if ($mergeModel->loadString($extension)) {
220
+ $mergeToObject->extend($mergeModel->getNode(), true);
221
+ }
222
+ }
223
+ }
224
+ return $mergeToObject;
225
+ }
226
+ public function getSortedXmlChildren($parent, $child, $select = '', $filter = array(), $defaultSortOrder = 0) {
227
+ $sortedResult = array();
228
+ $result = array();
229
+ if ($parent && isset($parent->$child)) {
230
+ foreach ($parent->$child->children() as $key => $options) {
231
+ if ($this->_doesXmlConformsFilter($options, $filter)) {
232
+ $sortOrder = isset($options->sort_order) ? (int)(string)$options->sort_order : $defaultSortOrder;
233
+ if ($sortOrder != 0) {
234
+ if (!isset($sortedResult[$sortOrder])) $sortedResult[$sortOrder] = array();
235
+ $sortedResult[$sortOrder][] = $key;
236
+ }
237
+ else {
238
+ $result[] = $key;
239
+ }
240
+ }
241
+ }
242
+ ksort($sortedResult);
243
+ $mergedResult = array();
244
+ foreach ($sortedResult as $prioritizedResult) {
245
+ $mergedResult = array_merge($mergedResult, $prioritizedResult);
246
+ }
247
+ $result = array_merge($mergedResult, $result);
248
+ }
249
+
250
+ $selectedResult = array();
251
+ if ($select) {
252
+ foreach ($result as $key) {
253
+ $selectedResult[$key] = (string)$parent->$child->$key->$select;
254
+ }
255
+ }
256
+ else {
257
+ foreach ($result as $key) {
258
+ $selectedResult[$key] = $parent->$child->$key;
259
+ }
260
+ }
261
+ return $selectedResult;
262
+ }
263
+ public function arrayFind($array, $column, $value)
264
+ {
265
+ foreach ($array as $index => $item) {
266
+ if ($item[$column] == $value) {
267
+ return $index;
268
+ }
269
+ }
270
+ return false;
271
+ }
272
+ public function collectionFind($collection, $column, $value)
273
+ {
274
+ $method = 'get'.$this->pascalCased($column);
275
+ foreach ($collection as $item) {
276
+ if ($item->$method() == $value) {
277
+ return $item;
278
+ }
279
+ }
280
+ return false;
281
+ }
282
+ public function countXmlChildren($xml, $filter = array()) {
283
+ $result = 0;
284
+ foreach ($xml->children() as $child) {
285
+ if ($this->_doesXmlConformsFilter($child, $filter)) {
286
+ $result++;
287
+ }
288
+ }
289
+ return $result;
290
+ }
291
+ protected function _doesXmlConformsFilter($xml, $filter) {
292
+ foreach ($filter as $field => $value) {
293
+ if (((string) ($xml->$field)) != $value) {
294
+ return false;
295
+ }
296
+ }
297
+ return true;
298
+ }
299
+ /**
300
+ * Returns rendered additional markup registered by extensions in configuration under $name key
301
+ * @param string $name
302
+ * @param array $parameters
303
+ * @return string
304
+ */
305
+ public function getNamedHtml($root, $name, $parameters = array()) {
306
+ $result = '';
307
+ foreach ($this->getSortedXmlChildren(Mage::getConfig()->getNode($root), $name) as $markup) {
308
+ $filename = Mage::getBaseDir('design').DS.
309
+ Mage::getDesign()->getTemplateFilename((string)$markup->template, array('_relative'=>true));
310
+ if (file_exists($filename)) {
311
+ $result .= $this->_fetchHtml($filename, $parameters);
312
+ }
313
+ }
314
+ return $result;
315
+ }
316
+ protected function _fetchHtml($filename, $parameters) {
317
+ extract ($parameters, EXTR_OVERWRITE);
318
+ ob_start();
319
+ try {
320
+ include $filename;
321
+ }
322
+ catch (Exception $e) {
323
+ ob_get_clean();
324
+ throw $e;
325
+ }
326
+ return ob_get_clean();
327
+ }
328
+ public function getJsPriceFormat() {
329
+ return $this->formatPrice(0);
330
+ }
331
+ public function formatPrice($price) {
332
+ $store = Mage::app()->getStore();
333
+ if ($store->getCurrentCurrency()) {
334
+ return $store->getCurrentCurrency()->formatPrecision($price, 0, array(), false, false);
335
+ }
336
+ return $price;
337
+ }
338
+ public function getIniByteValue($setting) {
339
+ $val = trim(ini_get($setting));
340
+ $last = strtolower($val[strlen($val)-1]);
341
+ switch($last) {
342
+ case 'g': $val *= 1024;
343
+ case 'm': $val *= 1024;
344
+ case 'k': $val *= 1024;
345
+ }
346
+ return $val;
347
+ }
348
+ public function jsonForceObjectAndEncode($data) {
349
+ return json_encode($this->_forceObjectRecursively($data));
350
+ }
351
+ protected function _forceObjectRecursively($data) {
352
+ if (is_array($data)) {
353
+ foreach ($data as $key => $value) {
354
+ $data[$key] = $this->_forceObjectRecursively($value);
355
+ }
356
+ return (object)$data;
357
+ }
358
+ elseif(is_object($data)) {
359
+ foreach ($data as $key => $value) {
360
+ $data->$key = $this->_forceObjectRecursively($value);
361
+ }
362
+ return $data;
363
+ }
364
+ else {
365
+ return $data;
366
+ }
367
+ }
368
+ public function getChildGroupHtml($block, $group) {
369
+ $result = '';
370
+ foreach ($block->getChildGroup($group, 'getChildHtml') as $alias => $html) {
371
+ $result .= $html;
372
+ }
373
+ return $result;
374
+ }
375
+ public function logProfiler($filename) {
376
+ $timers = Varien_Profiler::getTimers();
377
+
378
+ Mage::log('--------------------------------------------------', Zend_Log::DEBUG, $filename);
379
+ Mage::log("Code Profiler\tTime\tCnt\tEmalloc\tRealMem", Zend_Log::DEBUG, $filename);
380
+ foreach ($timers as $name => $timer) {
381
+ $sum = Varien_Profiler::fetch($name, 'sum');
382
+ $count = Varien_Profiler::fetch($name, 'count');
383
+ $realmem = Varien_Profiler::fetch($name, 'realmem');
384
+ $emalloc = Varien_Profiler::fetch($name, 'emalloc');
385
+ if ($sum < .0010 && $count < 10 && $emalloc < 10000) {
386
+ continue;
387
+ }
388
+ Mage::log(sprintf("%s\t%s\t%s\t%s\t%s",
389
+ $name, number_format($sum, 4), $count, number_format($emalloc), number_format($realmem)
390
+ ), Zend_Log::DEBUG, $filename);
391
+ }
392
+ }
393
+ public function getRoutePath($routePath = null) {
394
+ $request = Mage::app()->getRequest();
395
+ if ($routePath) {
396
+ $routePath = explode('/', $routePath);
397
+ if (isset($routePath[0]) && $routePath[0] == '*') $routePath[0] = $request->getRouteName();
398
+ if (isset($routePath[1]) && $routePath[1] == '*') $routePath[1] = $request->getControllerName();
399
+ if (isset($routePath[2]) && $routePath[2] == '*') $routePath[2] = $request->getActionName();
400
+ return $routePath[0] . (isset($routePath[1]) ? '/' . $routePath[1] : '') . (isset($routePath[2]) ? '/' . $routePath[2] : '');
401
+ }
402
+ else {
403
+ return $request->getRouteName() . '/' . $request->getControllerName() . '/' . $request->getActionName();
404
+ }
405
+ }
406
+ public function isMageVersionEqualOrGreater($version) {
407
+ $version = explode('.', $version);
408
+ $mageVersion = array_values(Mage::getVersionInfo());
409
+ foreach ($version as $key => $value) {
410
+ if ((int)$mageVersion[$key] < (int)$value) {
411
+ return false;
412
+ }
413
+ }
414
+ return true;
415
+ }
416
+ }
app/code/local/Mana/Core/Helper/Files.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Helper_Files extends Mage_Core_Helper_Abstract {
15
+ public function getFilename($relativeUrl, $type, $noExistanceCheck = false) {
16
+ $result = $this->getBasePath($type).DS.str_replace('/', DS, $relativeUrl);
17
+ if (!is_dir(dirname($result))) {
18
+ mkdir(dirname($result), 0777, true);
19
+ }
20
+ return $noExistanceCheck || file_exists($result) ? $result : false;
21
+ }
22
+ public function getBaseUrl($type) {
23
+ return Mage::getBaseUrl('media').'m-'.str_replace(DS, '/', $type);
24
+ }
25
+ public function getBasePath($type) {
26
+ return Mage::getConfig()->getOptions()->getMediaDir().DS.'m-'.str_replace('/', DS, $type);
27
+ }
28
+ public function getType($relativeUrl, $types) {
29
+ if (!is_array($types)) {
30
+ $types = array($types);
31
+ }
32
+ foreach ($types as $candidate) {
33
+ if ($filename = $this->getFilename($relativeUrl, $candidate)) {
34
+ return $candidate;
35
+ }
36
+ }
37
+ return false;
38
+ }
39
+ public function getUrl($relativeUrl, $type) {
40
+ if (is_array($type)) {
41
+ foreach ($type as $candidate) {
42
+ if ($url = $this->getUrl($relativeUrl, $candidate)) {
43
+ return $url;
44
+ }
45
+ }
46
+ return false;
47
+ }
48
+ else {
49
+ if ($this->getFilename($relativeUrl, $type)) {
50
+ return $this->getBaseUrl($type).'/'.str_replace(DS, '/', $relativeUrl);
51
+ }
52
+ else {
53
+ return false;
54
+ }
55
+ }
56
+ }
57
+ public function getNewUrl($filename, $type) {
58
+ $fileinfo = pathinfo(strtolower($filename));
59
+ $hash = sha1($fileinfo['filename']);
60
+ $first = substr($hash, strlen($hash) - 2, 1);
61
+ $second = substr($hash, strlen($hash) - 1);
62
+ $resultTemplate = "$first/$second/{$fileinfo['filename']}%s.{$fileinfo['extension']}";
63
+ $checkTemplate = $this->getFileName($resultTemplate, $type, true);
64
+ if (!file_exists(sprintf($checkTemplate, ''))) {
65
+ return sprintf($resultTemplate, '');
66
+ }
67
+ $i = 1;
68
+ while (true) {
69
+ if (!file_exists(sprintf($checkTemplate, '-'.$i))) {
70
+ return sprintf($resultTemplate, '-'.$i);
71
+ }
72
+ $i++;
73
+ }
74
+ }
75
+ }
app/code/local/Mana/Core/Helper/Js.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * Contains methods which makes javascript intensive programming easier.
10
+ * @author Mana Team
11
+ *
12
+ */
13
+ class Mana_Core_Helper_Js extends Mage_Core_Helper_Abstract {
14
+ /**
15
+ * Makes translations of specified strings to be available in client-side scripts.
16
+ * @param array $translations
17
+ * @return Mana_Core_Helper_Js
18
+ */
19
+ public function translations($translations) {
20
+ /* @var $layout Mage_Core_Model_Layout */ $layout = Mage::getSingleton(strtolower('Core/Layout'));
21
+ /* @var $jsBlock Mana_Core_Block_Js */ $jsBlock = $layout->getBlock('m_js');
22
+ $jsBlock->translations($translations);
23
+ return $this;
24
+ }
25
+ /**
26
+ * Makes options (specified in $options key-value pair array) for HTML element (selected with $selector)
27
+ * to be available in client-side scripts.
28
+ * @param string $selector
29
+ * @param array $options
30
+ * @return Mana_Core_Helper_Js
31
+ */
32
+ public function options($selector, $options) {
33
+ /* @var $layout Mage_Core_Model_Layout */ $layout = Mage::getSingleton(strtolower('Core/Layout'));
34
+ /* @var $jsBlock Mana_Core_Block_Js */ $jsBlock = $layout->getBlock('m_js');
35
+ $jsBlock->options($selector, $options);
36
+ return $this;
37
+ }
38
+ public function optionsToHtml($selector, $options) {
39
+ $options = json_encode(array($selector => $options));
40
+ return <<<EOT
41
+ <script type="text/javascript">
42
+ //<![CDATA[
43
+ jQuery.options($options);
44
+ //]]>
45
+ </script>
46
+ EOT;
47
+ }
48
+ }
app/code/local/Mana/Core/Helper/Lock.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Helper_Lock extends Mage_Core_Helper_Abstract {
15
+ protected $_locks = array();
16
+ protected $_isLocked = array();
17
+ /**
18
+ * Get lock file resource
19
+ *
20
+ * @return resource
21
+ */
22
+ protected function _getLockFile($name)
23
+ {
24
+ if (!isset($this->_locks[$name])) {
25
+ $varDir = Mage::getConfig()->getVarDir('locks');
26
+ $file = $varDir . DS . $name.'.lock';
27
+ if (is_file($file)) {
28
+ $this->_locks[$name] = fopen($file, 'w');
29
+ } else {
30
+ $this->_locks[$name] = fopen($file, 'x');
31
+ }
32
+ fwrite($this->_locks[$name], date('r'));
33
+ }
34
+ return $this->_locks[$name];
35
+ }
36
+
37
+ /**
38
+ * Lock process without blocking.
39
+ * This method allow protect multiple process runing and fast lock validation.
40
+ *
41
+ * @return Mage_Index_Model_Process
42
+ */
43
+ public function lock($name)
44
+ {
45
+ $this->_isLocked[$name] = true;
46
+ flock($this->_getLockFile($name), LOCK_EX | LOCK_NB);
47
+ return $this;
48
+ }
49
+
50
+ /**
51
+ * Lock and block process.
52
+ * If new instance of the process will try validate locking state
53
+ * script will wait until process will be unlocked
54
+ *
55
+ * @return Mage_Index_Model_Process
56
+ */
57
+ public function lockAndBlock($name)
58
+ {
59
+ $this->_isLocked[$name] = true;
60
+ flock($this->_getLockFile($name), LOCK_EX);
61
+ return $this;
62
+ }
63
+
64
+ /**
65
+ * Unlock process
66
+ *
67
+ * @return Mage_Index_Model_Process
68
+ */
69
+ public function unlock($name)
70
+ {
71
+ unset($this->_isLocked[$name]);
72
+ flock($this->_getLockFile($name), LOCK_UN);
73
+ fclose($this->_getLockFile($name));
74
+ unset($this->_locks[$name]);
75
+ return $this;
76
+ }
77
+
78
+ /**
79
+ * Check if process is locked
80
+ *
81
+ * @return bool
82
+ */
83
+ public function isLocked($name)
84
+ {
85
+ if (isset($this->_isLocked[$name])) {
86
+ return $this->_isLocked[$name];
87
+ } else {
88
+ $fp = $this->_getLockFile($name);
89
+ if (flock($fp, LOCK_EX | LOCK_NB)) {
90
+ flock($fp, LOCK_UN);
91
+ return false;
92
+ }
93
+ return true;
94
+ }
95
+ }
96
+ public function __destruct()
97
+ {
98
+ foreach ($this->_locks as $fh) {
99
+ fclose($fh);
100
+ }
101
+ }
102
+ }
app/code/local/Mana/Core/Model/Attribute/Scope.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Possible constants for attribute scope
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Attribute_Scope {
15
+ private function __construct() {}
16
+ const _STORE = 0;
17
+ const _GLOBAL = 1;
18
+
19
+ const _STORE_TEXT = 'store';
20
+ const _GLOBAL_TEXT = 'global';
21
+ const _SINGLE_STORE_TEXT = 'single_store';
22
+ }
app/code/local/Mana/Core/Model/Callback.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ * PROPERTY METHODS
14
+ *
15
+ *
16
+ * @method string | object getTarget()
17
+ * @method bool hasTarget(()
18
+ * @method Mana_Core_Model_Callback unsTarget(()
19
+ * @method Mana_Core_Model_Callback setTarget(()
20
+ *
21
+ *
22
+ * @method string getMethod()
23
+ * @method bool hasMethod(()
24
+ * @method Mana_Core_Model_Callback unsMethod(()
25
+ * @method Mana_Core_Model_Callback setMethod(()
26
+ *
27
+ */
28
+ class Mana_Core_Model_Callback extends Mana_Core_Model_Object {
29
+ public function callArray($args) {
30
+ if ($this->hasTarget()) {
31
+ return call_user_func_array(array($this->getTarget(), $this->getMethod()), $args);
32
+ }
33
+ else {
34
+ return call_user_func_array($this->getMethod(), $args);
35
+ }
36
+ }
37
+ public function call() {
38
+ return $this->callArray(func_get_args());
39
+ }
40
+ }
app/code/local/Mana/Core/Model/Config/Default.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Default value provider which gets value from global configuration
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Config_Default {
15
+ public function getDefaultValue($model, $attributeCode, $path) {
16
+ return Mage::getStoreConfig($path);
17
+ }
18
+ public function getUseDefaultLabel() {
19
+ return Mage::helper('mana_core')->__('Use System Configuration');
20
+ }
21
+ }
app/code/local/Mana/Core/Model/Eav.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for eav models which can handle store-scoped values as well as default for global scope
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Eav extends Mage_Core_Model_Abstract {
15
+ public function loadByAttribute($attribute, $value, $additionalAttributes='*')
16
+ {
17
+ $collection = $this->getResourceCollection()
18
+ ->setStoreId($this->getStoreId())
19
+ ->addAttributeToSelect($additionalAttributes)
20
+ ->addAttributeToFilter($attribute, $value)
21
+ ->setPage(1,1);
22
+
23
+ foreach ($collection as $object) {
24
+ return $object;
25
+ }
26
+ return $this->loadDefaults();
27
+ }
28
+ protected function _afterLoad() {
29
+ $this->loadDefaults();
30
+ parent::_afterLoad();
31
+ }
32
+ public function loadDefaults() {
33
+ $this->getResource()->loadDefaults($this);
34
+ return $this;
35
+ }
36
+ public function isDefaultValue($attribute) {
37
+ return (((int)$this->getData($attribute->getDefaultMaskField())) & ((int)$attribute->getDefaultMask())) == 0;
38
+ }
39
+
40
+ protected $_storeValueFlags;
41
+ public function isStoreValue($attribute) {
42
+ if (!$this->_storeValueFlags) $this->_storeValueFlags = array();
43
+ $key = $attribute->getAttributeCode().'_'.$this->getStoreId();
44
+ if (!isset($this->_storeValueFlags[$key])) {
45
+ $this->_storeValueFlags[$key] = $this->getResource()->hasStoreValue($this, $attribute);
46
+ }
47
+ return $this->_storeValueFlags[$key];
48
+ }
49
+ }
app/code/local/Mana/Core/Model/Html/Filter.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Basic HTML filter which output the same HTML as inputed
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Html_Filter extends Mana_Core_Model_Html_Parser {
15
+ protected $_filteredOutput = '';
16
+ public function getFilteredOutput() {
17
+ return $this->_filteredOutput;
18
+ }
19
+ protected function _processCDATA($parentElement, $content, $token) {
20
+ $this->_filteredOutput .= $token['full_text'];
21
+ }
22
+ protected function _processComment($parentElement, $content, $token) {
23
+ $this->_filteredOutput .= $token['full_text'];
24
+ }
25
+ protected function _processText($parentElement, $content, $token) {
26
+ $this->_filteredOutput .= $token['full_text'];
27
+ }
28
+ protected function _processElementName($parentContent, $element, $token, $elementName, $void, $rawText) {
29
+ $this->_filteredOutput .= '<'.$token['full_text'];
30
+ }
31
+ protected function _processAttributeName($parentContent, $element, $token, $attributeName) {
32
+ $this->_filteredOutput .= $token['full_text'];
33
+ }
34
+ protected function _processAttributeEq($parentContent, $element, $token) {
35
+ $this->_filteredOutput .= $token['full_text'];
36
+ }
37
+ protected function _processAttributeValue($parentContent, $element, $token, $attributeValue) {
38
+ $this->_filteredOutput .= $token['full_text'];
39
+ }
40
+ protected function _processElementClose($parentContent, $element, $token) {
41
+ $this->_filteredOutput .= $token['full_text'];
42
+ }
43
+ protected function _processElementEnd($parentContent, $element, $token, $elementName) {
44
+ $this->_filteredOutput .= '</'.$elementName.'>';
45
+ }
46
+ }
app/code/local/Mana/Core/Model/Html/Parser.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Implements naive HTML parser
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Html_Parser extends Varien_Object {
15
+ protected $_token;
16
+ protected $_openedElements = array();
17
+
18
+ protected function _construct() {
19
+ $this->_token = $this->getReader()->read($this->hasStartsWith() ? $this->getStartsWith() : Mana_Core_Model_Html_State::INITIAL_TEXT);
20
+ }
21
+ protected function _read($initialState, $expect = 0, $allowWhitespace = true) {
22
+ $this->_token = $this->getReader()->read($initialState, $allowWhitespace);
23
+ if ($expect && $this->_token['type'] != $expect) {
24
+ throw new Exception(Mage::helper('mana_core')->__('HTML parser error %s: %s expected%s',
25
+ Mana_Core_Model_Html_Token::getPosition($this->_token), Mana_Core_Model_Html_Token::getName($expect),
26
+ $this->getReader()->getSourceAt($this->_token)));
27
+ }
28
+ return $this->_token['type'];
29
+ }
30
+ /**
31
+ * Content ::= { Normal | SelfClosing | Void | TEXT | CDATA | COMMENT }
32
+ * Void ::= '<' (area | base | br | col | command | embed | hr | img | input | keygen | link | meta | param |
33
+ * source | track | wbr) Attributes ('>' | '/>')
34
+ * Normal ::= '<' NAME Attributes '/>' | ('>' Content '</' ID '>')
35
+ * Attributes ::= { NAME ['=' VALUE ] }
36
+ */
37
+ public function parseContent($parentElement = null) {
38
+ $result = $this->_beforeParsingContent($parentElement);
39
+ while ($this->_token['type'] != Mana_Core_Model_Html_Token::EOF && $this->_token['type'] != Mana_Core_Model_Html_Token::TAG_END) {
40
+ switch ($this->_token['type']) {
41
+ case Mana_Core_Model_Html_Token::TAG_START:
42
+ $this->_afterParsingChildElement($parentElement, $result, $this->parseElement($this->_beforeParsingChildElement($parentElement, $result)));
43
+ break;
44
+ case Mana_Core_Model_Html_Token::CDATA:
45
+ $this->_processCDATA($parentElement, $result, $this->_token);
46
+ $this->_read(Mana_Core_Model_Html_State::INITIAL_TEXT);
47
+ break;
48
+ case Mana_Core_Model_Html_Token::COMMENT:
49
+ $this->_processComment($parentElement, $result, $this->_token);
50
+ $this->_read(Mana_Core_Model_Html_State::INITIAL_TEXT);
51
+ break;
52
+ case Mana_Core_Model_Html_Token::TEXT:
53
+ $this->_processText($parentElement, $result, $this->_token);
54
+ $this->_read(Mana_Core_Model_Html_State::INITIAL_TEXT);
55
+ break;
56
+ default:
57
+ throw new Exception(Mage::helper('mana_core')->__('HTML parser error %s: unexpected %s%s',
58
+ Mana_Core_Model_Html_Token::getPosition($this->_token),
59
+ Mana_Core_Model_Html_Token::getName($this->_token['type']),
60
+ $this->getReader()->getSourceAt($this->_token)));
61
+ }
62
+ }
63
+ return $this->_afterParsingContent($parentElement, $result);
64
+ }
65
+ public function parseElement($parentContent = null) {
66
+ $result = $this->_beforeParsingElement($parentContent);
67
+ $this->_read(Mana_Core_Model_Html_State::INITIAL, Mana_Core_Model_Html_Token::NAME, false);
68
+ $elementName = $this->_token['text'];
69
+ array_push($this->_openedElements, $elementName);
70
+ $void = Mana_Core_Model_Html_Token::isVoid($elementName);
71
+ $rawText = Mana_Core_Model_Html_Token::isRawText($elementName);
72
+ $this->_processElementName($parentContent, $result, $this->_token, $elementName, $void, $rawText);
73
+ while ($this->_read(Mana_Core_Model_Html_State::INITIAL) == Mana_Core_Model_Html_Token::NAME || ($this->_token['type'] == Mana_Core_Model_Html_Token::EQ)) {
74
+ if ($this->_token['type'] != Mana_Core_Model_Html_Token::EQ) {
75
+ $attributeName = $this->_token['text'];
76
+ $this->_processAttributeName($parentContent, $result, $this->_token, $attributeName);
77
+ }
78
+ else {
79
+ $this->_processAttributeEq($parentContent, $result, $this->_token);
80
+ $this->_read(Mana_Core_Model_Html_State::INITIAL_VALUE, Mana_Core_Model_Html_Token::VALUE, true);
81
+ $attributeValue = $this->_token['text'];
82
+ $this->_processAttributeValue($parentContent, $result, $this->_token, $attributeValue);
83
+ }
84
+ }
85
+ switch ($this->_token['type']) {
86
+ case Mana_Core_Model_Html_Token::TAG_SELF_CLOSE:
87
+ $this->_processElementClose($parentContent, $result, $this->_token);
88
+ array_pop($this->_openedElements);
89
+ break;
90
+ case Mana_Core_Model_Html_Token::TAG_CLOSE:
91
+ $this->_processElementClose($parentContent, $result, $this->_token);
92
+ if (!$void) {
93
+ $this->_read($rawText ? $elementName : Mana_Core_Model_Html_State::INITIAL_TEXT);
94
+ $this->_afterParsingChildContent($parentContent, $result, $this->parseContent($this->_beforeParsingChildContent($parentContent, $result)));
95
+ if ($this->_token['type'] != Mana_Core_Model_Html_Token::TAG_END) {
96
+ throw new Exception(Mage::helper('mana_core')->__('HTML parser error %s: %s expected%s',
97
+ Mana_Core_Model_Html_Token::getPosition($this->_token),
98
+ Mana_Core_Model_Html_Token::getName(Mana_Core_Model_Html_Token::TAG_END),
99
+ $this->getReader()->getSourceAt($this->_token)));
100
+ }
101
+ $this->_read(Mana_Core_Model_Html_State::INITIAL, Mana_Core_Model_Html_Token::NAME, false);
102
+ array_pop($this->_openedElements);
103
+ if ($this->_token['text'] != $elementName) {
104
+ if (in_array($this->_token['text'], $this->_openedElements)) {
105
+ $this->getReader()->move(-3 - mb_strlen($this->_token['text']));
106
+ $this->_processElementEnd($parentContent, $result, $this->_token, $elementName);
107
+ }
108
+ else {
109
+ throw new Exception(Mage::helper('mana_core')->__('HTML parser error %s: closing tag for %s expected%s',
110
+ Mana_Core_Model_Html_Token::getPosition($this->_token), $elementName,
111
+ $this->getReader()->getSourceAt($this->_token)));
112
+ }
113
+ }
114
+ else {
115
+ $this->_read(Mana_Core_Model_Html_State::INITIAL, Mana_Core_Model_Html_Token::TAG_CLOSE, false);
116
+ $this->_processElementEnd($parentContent, $result, $this->_token, $elementName);
117
+ }
118
+ }
119
+ else {
120
+ array_pop($this->_openedElements);
121
+ }
122
+ break;
123
+ default:
124
+ throw new Exception(Mage::helper('mana_core')->__('HTML parser error %s: %s or %s expected%s',
125
+ Mana_Core_Model_Html_Token::getPosition($this->_token),
126
+ Mana_Core_Model_Html_Token::getName(Mana_Core_Model_Html_Token::TAG_SELF_CLOSE),
127
+ Mana_Core_Model_Html_Token::getName(Mana_Core_Model_Html_Token::TAG_CLOSE),
128
+ $this->getReader()->getSourceAt($this->_token)));
129
+ }
130
+ $this->_read(Mana_Core_Model_Html_State::INITIAL_TEXT);
131
+ return $this->_afterParsingElement($parentContent, $result);
132
+ }
133
+
134
+ /* CONTENT CALLBACKS */
135
+
136
+ protected function _beforeParsingContent($parentElement) {
137
+ return null;
138
+ }
139
+ protected function _afterParsingContent($parentElement, $content) {
140
+ return $content;
141
+ }
142
+ protected function _beforeParsingChildElement($parentElement, $content) {
143
+ return $content;
144
+ }
145
+ protected function _afterParsingChildElement($parentElement, $content, $childElement) {
146
+ }
147
+ protected function _processCDATA($parentElement, $content, $token) {
148
+ }
149
+ protected function _processComment($parentElement, $content, $token) {
150
+ }
151
+ protected function _processText($parentElement, $content, $token) {
152
+ }
153
+
154
+ /* ELEMENT CALLBACKS */
155
+
156
+ protected function _beforeParsingElement($parentContent) {
157
+ return null;
158
+ }
159
+ protected function _afterParsingElement($parentContent, $element) {
160
+ return $element;
161
+ }
162
+ protected function _processElementName($parentContent, $element, $token, $elementName, $void, $rawText) {
163
+ }
164
+ protected function _processAttributeName($parentContent, $element, $token, $attributeName) {
165
+ }
166
+ protected function _processAttributeEq($parentContent, $element, $token) {
167
+ }
168
+ protected function _processAttributeValue($parentContent, $element, $token, $attributeValue) {
169
+ }
170
+ protected function _processElementClose($parentContent, $element, $token) {
171
+ }
172
+ protected function _beforeParsingChildContent($parentContent, $element) {
173
+ return $element;
174
+ }
175
+ protected function _afterParsingChildContent($parentContent, $element, $childContent) {
176
+ }
177
+ protected function _processElementEnd($parentContent, $element, $token, $elementName) {
178
+ }
179
+ }
app/code/local/Mana/Core/Model/Html/Reader.php ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Naive HTML tokenizer
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Html_Reader {
15
+ protected $_source;
16
+ protected $_state;
17
+ protected $_pos;
18
+ protected $_ch;
19
+ protected $_length;
20
+ protected $_tabWidth = 4;
21
+ protected $_line;
22
+ protected $_column;
23
+ public function setSource($source) {
24
+ $this->_source = $source;
25
+ $this->_state = Mana_Core_Model_Html_State::INITIAL;
26
+ $this->_length = mb_strlen($source);
27
+ $this->_pos = -1;
28
+ $this->_line = 1;
29
+ $this->_column = 0;
30
+ $this->_ch = $this->_read();
31
+ return $this;
32
+ }
33
+
34
+ public function read($initialState, $allowWhitespace = true) {
35
+ if (is_string($initialState)) {
36
+ $state = Mana_Core_Model_Html_State::INITIAL_RAWTEXT;
37
+ $rawElement = strtolower($initialState);
38
+ }
39
+ else {
40
+ $state = $initialState;
41
+ }
42
+ $startPos = $this->_pos;
43
+ $token = array(
44
+ 'pos' => $this->_pos,
45
+ 'line' => $this->_line,
46
+ 'column' => $this->_column,
47
+ // other valid indexes: type, text, full_text
48
+ );
49
+ $readNext = true;
50
+ while ($state != Mana_Core_Model_Html_State::FINISHED) {
51
+ $ch = $this->_ch !== false ? ord($this->_ch) : false;
52
+ switch ($state) {
53
+ case Mana_Core_Model_Html_State::INITIAL_TEXT:
54
+ if ($ch === false) {
55
+ $token['type'] = Mana_Core_Model_Html_Token::EOF;
56
+ $readNext = false;
57
+ $state = Mana_Core_Model_Html_State::FINISHED;
58
+ }
59
+ elseif ($ch == ord('<')) {
60
+ // here we assume we have enough characters in read buffer. It is always the case for now,
61
+ // later it may break if we work with underlying stream, not memory buffer
62
+ if (mb_substr($this->_source, $this->_pos, 2) == '</') {
63
+ $this->_move(1);
64
+ $token['type'] = Mana_Core_Model_Html_Token::TAG_END;
65
+ $state = Mana_Core_Model_Html_State::FINISHED;
66
+ }
67
+ elseif (mb_substr($this->_source, $this->_pos, 8) == '<![CDATA') {
68
+ $this->_move(7);
69
+ $token['type'] = Mana_Core_Model_Html_Token::CDATA;
70
+ $state = Mana_Core_Model_Html_State::CDATA;
71
+ }
72
+ elseif (mb_substr($this->_source, $this->_pos, 4) == '<!--') {
73
+ $this->_move(3);
74
+ $token['type'] = Mana_Core_Model_Html_Token::COMMENT;
75
+ $state = Mana_Core_Model_Html_State::COMMENT;
76
+ }
77
+ else {
78
+ $token['type'] = Mana_Core_Model_Html_Token::TAG_START;
79
+ $state = Mana_Core_Model_Html_State::FINISHED;
80
+ }
81
+ }
82
+ else {
83
+ $token['type'] = Mana_Core_Model_Html_Token::TEXT;
84
+ $state = Mana_Core_Model_Html_State::TEXT;
85
+ }
86
+ break;
87
+
88
+ case Mana_Core_Model_Html_State::INITIAL:
89
+ if ($ch == ord(' ') || $ch == ord("\r") || $ch == ord("\t") || $ch == ord("\n") || $ch == ord("\f")) {
90
+ if (!$allowWhitespace) {
91
+ $token['end_pos'] = $this->_pos;
92
+ throw new Exception(Mage::helper('mana_core')->__('HTML read error %s: whitespace not expected%s',
93
+ Mana_Core_Model_Html_Token::getPosition($token),
94
+ $this->getSourceAt($token)));
95
+ }
96
+ }
97
+ else {
98
+ $token['pos'] = $this->_pos;
99
+ $token['line'] = $this->_line;
100
+ $token['column'] = $this->_column;
101
+ if ($ch === false) {
102
+ $token['type'] = Mana_Core_Model_Html_Token::EOF;
103
+ $readNext = false;
104
+ $state = Mana_Core_Model_Html_State::FINISHED;
105
+ }
106
+ elseif ($ch == ord('>')) {
107
+ $token['type'] = Mana_Core_Model_Html_Token::TAG_CLOSE;
108
+ $state = Mana_Core_Model_Html_State::FINISHED;
109
+ }
110
+ elseif($ch == ord('/') && mb_substr($this->_source, $this->_pos, 2) == '/>') {
111
+ $this->_move(1);
112
+ $token['type'] = Mana_Core_Model_Html_Token::TAG_SELF_CLOSE;
113
+ $state = Mana_Core_Model_Html_State::FINISHED;
114
+ }
115
+ elseif ($ch == ord('=')) {
116
+ $token['type'] = Mana_Core_Model_Html_Token::EQ;
117
+ $state = Mana_Core_Model_Html_State::FINISHED;
118
+ }
119
+ elseif ($ch == ord('!') || ord('a') <= $ch && $ch <= ord('z') || ord('A') <= $ch && $ch <= ord('Z')) {
120
+ $state = Mana_Core_Model_Html_State::NAME;
121
+ $token['type'] = Mana_Core_Model_Html_Token::NAME;
122
+ }
123
+ else {
124
+ $token['end_pos'] = $this->_pos;
125
+ throw new Exception(Mage::helper('mana_core')->__('HTML read error %s: unexpected character%s',
126
+ Mana_Core_Model_Html_Token::getPosition($token),
127
+ $this->getSourceAt($token)));
128
+ }
129
+ }
130
+ break;
131
+ case Mana_Core_Model_Html_State::INITIAL_VALUE:
132
+ if ($ch == ord(' ') || $ch == ord("\r") || $ch == ord("\t") || $ch == ord("\n") || $ch == ord("\f")) {
133
+ if (!$allowWhitespace) {
134
+ $token['end_pos'] = $this->_pos;
135
+ throw new Exception(Mage::helper('mana_core')->__('HTML read error %s: whitespace not expected%s',
136
+ Mana_Core_Model_Html_Token::getPosition($token),
137
+ $this->getSourceAt($token)));
138
+ }
139
+ }
140
+ else {
141
+ $token['pos'] = $this->_pos;
142
+ $token['line'] = $this->_line;
143
+ $token['column'] = $this->_column;
144
+ $token['type'] = Mana_Core_Model_Html_Token::VALUE;
145
+ if ($ch === false) {
146
+ $token['type'] = Mana_Core_Model_Html_Token::EOF;
147
+ $readNext = false;
148
+ $state = Mana_Core_Model_Html_State::FINISHED;
149
+ }
150
+ elseif ($ch == ord("'")) {
151
+ $state = Mana_Core_Model_Html_State::SINGLE_QUOTED_VALUE;
152
+ $token['pos']++; $token['column']++;
153
+ }
154
+ elseif ($ch == ord('"')) {
155
+ $state = Mana_Core_Model_Html_State::DOUBLE_QUOTED_VALUE;
156
+ $token['pos']++; $token['column']++;
157
+ }
158
+ elseif (!($ch == ord('=') || $ch == ord('<') || $ch == ord('>') || $ch == ord('`'))) {
159
+ $state = Mana_Core_Model_Html_State::UNQUOTED_VALUE;
160
+ }
161
+ else {
162
+ $token['end_pos'] = $this->_pos;
163
+ throw new Exception(Mage::helper('mana_core')->__('HTML read error %s: unexpected character%s',
164
+ Mana_Core_Model_Html_Token::getPosition($token),
165
+ $this->getSourceAt($token)));
166
+ }
167
+ }
168
+ break;
169
+ case Mana_Core_Model_Html_State::INITIAL_RAWTEXT:
170
+ if ($ch === false) {
171
+ $token['type'] = Mana_Core_Model_Html_Token::EOF;
172
+ $readNext = false;
173
+ $state = Mana_Core_Model_Html_State::FINISHED;
174
+ }
175
+ else {
176
+ $token['type'] = Mana_Core_Model_Html_Token::TEXT;
177
+ $state = Mana_Core_Model_Html_State::RAWTEXT;
178
+ if ($ch == ord('<') && strtolower(mb_substr($this->_source, $this->_pos, 2 + mb_strlen($rawElement))) == '</'.$rawElement) {
179
+ if (mb_strlen($this->_source) > $this->_pos + 2 + mb_strlen($rawElement)) {
180
+ $nextCh = ord(mb_substr($this->_source, $this->_pos + 2 + mb_strlen($rawElement), 1));
181
+ if ($nextCh == ord('>') || $nextCh == ord('/') ||
182
+ $ch == ord(' ') || $ch == ord("\r") || $ch == ord("\t") || $ch == ord("\n") || $ch == ord("\f"))
183
+ {
184
+ $readNext = false;
185
+ $state = Mana_Core_Model_Html_State::FINISHED;
186
+ }
187
+ }
188
+ }
189
+ }
190
+ break;
191
+
192
+ case Mana_Core_Model_Html_State::CDATA:
193
+ if ($ch === false) {
194
+ $token['end_pos'] = $this->_pos;
195
+ throw new Exception(Mage::helper('mana_core')->__('HTML read error %s: unexpected end of text%s',
196
+ Mana_Core_Model_Html_Token::getPosition($token),
197
+ $this->getSourceAt($token)));
198
+ }
199
+ elseif ($ch == ord(']') && mb_substr($this->_source, $this->_pos, 3) == ']]>') {
200
+ $this->_move(2);
201
+ $state = Mana_Core_Model_Html_State::FINISHED;
202
+ }
203
+ break;
204
+ case Mana_Core_Model_Html_State::COMMENT:
205
+ if ($ch === false) {
206
+ $token['end_pos'] = $this->_pos;
207
+ throw new Exception(Mage::helper('mana_core')->__('HTML read error %s: unexpected end of text%s',
208
+ Mana_Core_Model_Html_Token::getPosition($token),
209
+ $this->getSourceAt($token)));
210
+ }
211
+ elseif ($ch == ord('-') && mb_substr($this->_source, $this->_pos, 3) == '-->') {
212
+ $this->_move(2);
213
+ $state = Mana_Core_Model_Html_State::FINISHED;
214
+ }
215
+ break;
216
+ case Mana_Core_Model_Html_State::TEXT:
217
+ if ($ch === false || $ch == ord('<')) {
218
+ $readNext = false;
219
+ $state = Mana_Core_Model_Html_State::FINISHED;
220
+ }
221
+ break;
222
+ case Mana_Core_Model_Html_State::RAWTEXT:
223
+ if ($ch === false) {
224
+ $readNext = false;
225
+ $state = Mana_Core_Model_Html_State::FINISHED;
226
+ }
227
+ elseif ($ch == ord('<') && strtolower(mb_substr($this->_source, $this->_pos, 2 + mb_strlen($rawElement))) == '</'.$rawElement) {
228
+ if (mb_strlen($this->_source) > $this->_pos + 2 + mb_strlen($rawElement)) {
229
+ $nextCh = ord(mb_substr($this->_source, $this->_pos + 2 + mb_strlen($rawElement), 1));
230
+ if ($nextCh == ord('>') || $nextCh == ord('/') ||
231
+ $ch == ord(' ') || $ch == ord("\r") || $ch == ord("\t") || $ch == ord("\n") || $ch == ord("\f"))
232
+ {
233
+ $readNext = false;
234
+ $state = Mana_Core_Model_Html_State::FINISHED;
235
+ }
236
+ }
237
+ }
238
+ break;
239
+
240
+ case Mana_Core_Model_Html_State::NAME:
241
+ if ($ch === false || !($ch == ord('!') || ord('a') <= $ch && $ch <= ord('z') || ord('A') <= $ch && $ch <= ord('Z')
242
+ || $ch == ord('_') || $ch == ord('-') || $ch == ord(':') || ord('0') <= $ch && $ch <= ord('9')))
243
+ {
244
+ $readNext = false;
245
+ $state = Mana_Core_Model_Html_State::FINISHED;
246
+ }
247
+ break;
248
+ case Mana_Core_Model_Html_State::UNQUOTED_VALUE:
249
+ if ($ch === false || $ch == ord('"') || $ch == ord("'") ||
250
+ $ch == ord(' ') || $ch == ord("\r") || $ch == ord("\t") || $ch == ord("\n") || $ch == ord("\f") ||
251
+ $ch == ord('=') || $ch == ord('<') || $ch == ord('>') || $ch == ord('`'))
252
+ {
253
+ $readNext = false;
254
+ $state = Mana_Core_Model_Html_State::FINISHED;
255
+ }
256
+ break;
257
+ case Mana_Core_Model_Html_State::SINGLE_QUOTED_VALUE:
258
+ if ($ch === false) {
259
+ $token['end_pos'] = $this->_pos;
260
+ throw new Exception(Mage::helper('mana_core')->__('HTML read error %s: unexpected end of text%s',
261
+ Mana_Core_Model_Html_Token::getPosition($token),
262
+ $this->getSourceAt($token)));
263
+ }
264
+ elseif ($ch == ord("'")) {
265
+ $state = Mana_Core_Model_Html_State::FINISHED;
266
+ $token['end_pos'] = $this->_pos;
267
+ }
268
+ break;
269
+ case Mana_Core_Model_Html_State::DOUBLE_QUOTED_VALUE:
270
+ if ($ch === false) {
271
+ $token['end_pos'] = $this->_pos;
272
+ throw new Exception(Mage::helper('mana_core')->__('HTML read error %s: unexpected end of text%s',
273
+ Mana_Core_Model_Html_Token::getPosition($token),
274
+ $this->getSourceAt($token)));
275
+ }
276
+ elseif ($ch == ord('"')) {
277
+ $state = Mana_Core_Model_Html_State::FINISHED;
278
+ $token['end_pos'] = $this->_pos;
279
+ }
280
+ break;
281
+
282
+ default: throw new Exception('Not implemented');
283
+ }
284
+ if ($readNext) $this->_ch = $this->_read();
285
+ }
286
+ if (!isset($token['end_pos'])) {
287
+ $token['end_pos'] = $this->_pos;
288
+ }
289
+ $token['text'] = $token['end_pos'] == $token['pos'] ? '' : mb_substr($this->_source, $token['pos'], $token['end_pos'] - $token['pos']);
290
+ $token['full_text'] = $this->_pos == $startPos ? '' : mb_substr($this->_source, $startPos, $this->_pos - $startPos);
291
+ return $token;
292
+ }
293
+
294
+ protected function _read() {
295
+ $ch = ++$this->_pos < $this->_length ? mb_substr($this->_source, $this->_pos, 1) : false;
296
+ if ($ch == "\n") {
297
+ $this->_line++;
298
+ $this->_column = 0;
299
+ }
300
+ elseif ($ch == "\t") {
301
+ $this->_column += $this->_tabWidth;
302
+ }
303
+ else {
304
+ $this->_column++;
305
+ }
306
+ return $ch;
307
+ }
308
+ protected function _move($offset) {
309
+ $this->_pos += $offset;
310
+ $this->_column += $offset;
311
+ }
312
+ public function move($offset) {
313
+ $this->_move($offset);
314
+ $this->_ch = $this->_read();
315
+ }
316
+ public function getSourceAt($token) {
317
+ return Mana_Core_Model_Html_Token::getSourceAt($this->_source, $token, $this->_tabWidth);
318
+ }
319
+ }
app/code/local/Mana/Core/Model/Html/State.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * HTML scanner states
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Html_State {
15
+ // initial states
16
+ const INITIAL = 0; // default, should recognize name, =, >, />
17
+ const INITIAL_TEXT = 1;
18
+ const INITIAL_VALUE = 2;
19
+ const INITIAL_RAWTEXT = 3;
20
+
21
+ // text states
22
+ const CDATA = 5;
23
+ const COMMENT = 6;
24
+ const TEXT = 7;
25
+ const RAWTEXT = 8;
26
+
27
+ // element states
28
+ const NAME = 9;
29
+ const SINGLE_QUOTED_VALUE = 10;
30
+ const DOUBLE_QUOTED_VALUE = 11;
31
+ const UNQUOTED_VALUE = 12;
32
+
33
+ // final state
34
+ const FINISHED = 99;
35
+ }
app/code/local/Mana/Core/Model/Html/Token.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * HTML tokens recognized by scanner
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Html_Token {
15
+ protected static $_sourceLinesBefore = 5;
16
+ const NOTHING = 0;
17
+ const EOF = 1;
18
+ const TAG_START = 2; // <
19
+ const TAG_END = 3; // </
20
+ const TAG_SELF_CLOSE = 4; // />
21
+ const TAG_CLOSE = 5; // >
22
+ const EQ = 6; // =
23
+ const CDATA = 7; // <![CDATA[ some text ]]>
24
+ const COMMENT = 8; // <!-- some text -->
25
+ const TEXT = 9;
26
+ const NAME = 10;
27
+ const VALUE = 11; // value | 'value' | "value"
28
+
29
+ protected static $_names = array(
30
+ self::NOTHING => 'nothing',
31
+ self::EOF => 'end-of-file',
32
+ self::TAG_START => '"<"',
33
+ self::TAG_END => '"</"',
34
+ self::TAG_SELF_CLOSE => '"/>"',
35
+ self::TAG_CLOSE => '">"',
36
+ self::EQ => '"="',
37
+ self::CDATA => 'CDATA section (<![CDATA[ ]]>)',
38
+ self::COMMENT => 'comment (<!-- -->)',
39
+ self::TEXT => 'raw text',
40
+ self::NAME => 'element or attribute name',
41
+ self::VALUE => 'attribute value',
42
+ );
43
+ public static function getName($token) {
44
+ return Mage::helper('mana_core')->__(self::$_names[$token]);
45
+ }
46
+ protected static $_voidElements = array('area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img',
47
+ 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr', '!doctype');
48
+ protected static $_rawTextElements = array('script', 'style', 'title', 'textarea');
49
+ public static function isVoid($elementName) {
50
+ return in_array(strtolower($elementName), self::$_voidElements);
51
+ }
52
+ public static function isRawText($elementName) {
53
+ return in_array(strtolower($elementName), self::$_rawTextElements);
54
+ }
55
+ public static function getPosition($token) {
56
+ return sprintf('(%s, %s)', $token['column'], $token['line']);
57
+ }
58
+ public static function getSourceAt(&$source, $token, $tabWidth) {
59
+ $result = "\n";
60
+ $lines = explode("\n", str_replace("\t", str_repeat(' ', $tabWidth), $source));
61
+ for ($i = 0; $i < self::$_sourceLinesBefore; $i++) {
62
+ $line = $token['line'] - (self::$_sourceLinesBefore - $i + 1);
63
+ if ($line >= 0) {
64
+ $result .= $lines[$line]."\n";
65
+ }
66
+ }
67
+ $result .= $lines[$token['line'] - 1]."\n";
68
+ if ($token['column'] > 1) {
69
+ $result .= str_repeat(' ', $token['column'] - 1);
70
+ }
71
+ $result .= str_repeat('-', $token['end_pos'] <= $token['pos'] ? 1 :
72
+ ($token['end_pos'] - $token['pos'] + $token['column'] - 1 >= mb_strlen($lines[$token['line'] - 1]) ?
73
+ mb_strlen($lines[$token['line'] - 1]) - ($token['column'] - 1) :
74
+ $token['end_pos'] - $token['pos']));
75
+ if ($token['line'] < count($lines)) {
76
+ $result .= "\n".$lines[$token['line']];
77
+ }
78
+ return $result;
79
+ }
80
+ }
app/code/local/Mana/Core/Model/Object.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Object extends Varien_Object {
15
+ protected $_collections = array();
16
+ protected $_arrays = array();
17
+
18
+ public function getCollectionItems($collection) {
19
+ return $this->_collections[$collection];
20
+ }
21
+ public function getCollectionItem($collection, $key) {
22
+ return $this->_collections[$collection][$key];
23
+ }
24
+ public function hasCollectionItem($collection, $key) {
25
+ return isset($this->_collections[$collection][$key]);
26
+ }
27
+ public function setCollectionItem($collection, $key, $value) {
28
+ $this->_collections[$collection][$key] = $value;
29
+ return $this;
30
+ }
31
+ public function setCollectionItems($collection, $items) {
32
+ $this->_collections[$collection] = $items;
33
+ return $this;
34
+ }
35
+ public function unsCollectionItem($collection, $key) {
36
+ unset($this->_collections[$collection][$key]);
37
+ return $this;
38
+ }
39
+
40
+ public function getArrayItems($array) {
41
+ return $this->_arrays[$array];
42
+ }
43
+ public function getArrayItem($array, $index) {
44
+ return $this->_arrays[$array][$index];
45
+ }
46
+ public function unsArrayItems($array) {
47
+ $this->_arrays[$array] = array();
48
+ return $this;
49
+ }
50
+ public function unsArrayItem($array, $index) {
51
+ unset($this->_arrays[$array][$index]);
52
+ return $this;
53
+ }
54
+ public function addArrayItem($array, $value) {
55
+ $this->_arrays[$array][] = $value;
56
+ return $this;
57
+ }
58
+ public function setArrayItems($array, $items) {
59
+ $this->_arrays[$array] = $items;
60
+ return $this;
61
+ }
62
+
63
+ protected function _toArrayExRecursively($array) {
64
+ $result = array();
65
+ foreach ($array as $key => $value) {
66
+ $result[$key] = ($value instanceof Mana_Core_Model_Object) ?
67
+ array('__M_ARRAY_EX' => $value->toArrayEx(), '__M_CLASS' => get_class($value)) :
68
+ $value;
69
+ }
70
+ return $result;
71
+ }
72
+ protected function _fromArrayExRecursively($array) {
73
+ $result = array();
74
+ foreach ($array as $key => $value) {
75
+ if (is_array($value) && isset($value['__M_CLASS']) && isset($value['__M_ARRAY_EX'])) {
76
+ $class = $value['__M_CLASS'];
77
+ $object = new $class;
78
+ $object->fromArrayEx($value['__M_ARRAY_EX']);
79
+ $result[$key] = $object;
80
+ }
81
+ else {
82
+ $result[$key] = $value;
83
+ }
84
+ }
85
+ return $result;
86
+ }
87
+ public function toArrayEx() {
88
+ return array(
89
+ 'data' => $this->_toArrayExRecursively($this->_data),
90
+ 'collections' => $this->_toArrayExRecursively($this->_collections),
91
+ 'arrays' => $this->_toArrayExRecursively($this->_arrays),
92
+ );
93
+ }
94
+ public function fromArrayEx($array) {
95
+ $this->_data = $this->_fromArrayExRecursively($array['data']);
96
+ $this->_collections = $this->_fromArrayExRecursively($array['collections']);
97
+ $this->_arrays = $this->_fromArrayExRecursively($array['arrays']);
98
+ return $this;
99
+ }
100
+ public function toJsonEx() {
101
+ return Zend_Json::encode($this->toArrayEx());
102
+ }
103
+ public function fromJsonEx($json) {
104
+ $this->fromArrayEx(Zend_Json::decode($json));
105
+ }
106
+ public function __call($method, $args) {
107
+ if (substr($method, 0, 3) == 'get') {
108
+ $key = $this->_underscore(substr($method,3));
109
+ if (isset($this->_collections[$key])) {
110
+ return $this->_collections[$key];
111
+ }
112
+ if (isset($this->_collections[$key.'s'])) {
113
+ return $this->_collections[$key.'s'][$args[0]];
114
+ }
115
+ if (isset($this->_arrays[$key])) {
116
+ return $this->_arrays[$key];
117
+ }
118
+ if (isset($this->_arrays[$key.'s'])) {
119
+ return $this->_arrays[$key.'s'][$args[0]];
120
+ }
121
+ }
122
+ elseif (substr($method, 0, 3) == 'has') {
123
+ $key = $this->_underscore(substr($method,3));
124
+ if (isset($this->_collections[$key.'s'])) {
125
+ return isset($this->_collections[$key.'s'][$args[0]]);
126
+ }
127
+ }
128
+ elseif (substr($method, 0, 3) == 'set') {
129
+ $key = $this->_underscore(substr($method,3));
130
+ if (isset($this->_collections[$key])) {
131
+ $this->_collections[$key] = $args[0];
132
+ return $this;
133
+ }
134
+ if (isset($this->_collections[$key.'s'])) {
135
+ $this->_collections[$key.'s'][$args[0]] = $args[1];
136
+ return $this;
137
+ }
138
+ if (isset($this->_arrays[$key])) {
139
+ $this->_arrays[$key] = $args[0];
140
+ return $this;
141
+ }
142
+ }
143
+ elseif (substr($method, 0, 3) == 'add') {
144
+ $key = $this->_underscore(substr($method,3));
145
+ if (isset($this->_arrays[$key.'s'])) {
146
+ $this->_arrays[$key.'s'][] = $args[0];
147
+ return $this;
148
+ }
149
+ }
150
+ elseif (substr($method, 0, 3) == 'uns') {
151
+ $key = $this->_underscore(substr($method,3));
152
+ if (isset($this->_collections[$key.'s'])) {
153
+ unset($this->_collections[$key.'s'][$args[0]]);
154
+ return $this;
155
+ }
156
+ if (isset($this->_arrays[$key])) {
157
+ $this->_arrays[$key] = array();
158
+ return $this;
159
+ }
160
+ if (isset($this->_arrays[$key.'s'])) {
161
+ unset($this->_arrays[$key.'s'][$args[0]]);
162
+ return $this;
163
+ }
164
+ }
165
+ return parent::__call($method, $args);
166
+ }
167
+ }
app/code/local/Mana/Core/Model/Observer.php ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Models/Observer */
10
+ /**
11
+ * This class observes certain (defined in etc/config.xml) events in the whole system and provides public methods - handlers for
12
+ * these events.
13
+ * @author Mana Team
14
+ *
15
+ */
16
+ class Mana_Core_Model_Observer {
17
+ /* BASED ON SNIPPET: Models/Event handler */
18
+ /**
19
+ * Add layout handles from config.xml files (handles event "controller_action_layout_load_before")
20
+ * @param Varien_Event_Observer $observer
21
+ */
22
+ public function addLayoutHandles($observer) {
23
+ /* @var $action Mage_Core_Controller_Varien_Action */ $action = $observer->getEvent()->getAction();
24
+ /* @var $layout Mage_Core_Model_Layout */ $layout = $observer->getEvent()->getLayout();
25
+
26
+ if (Mage::getConfig()->getNode('m_layout')) {
27
+ foreach (Mage::getConfig()->getNode('m_layout')->children() as $name => $config) {
28
+ if (in_array($name, $layout->getUpdate()->getHandles())) {
29
+ foreach ($config->children() as $action => $actionConfig) {
30
+ if (isset($actionConfig['if'])) {
31
+ $method = (string) $actionConfig['if'];
32
+ $args = array();
33
+ foreach ($actionConfig->children() as $arg) {
34
+ $args[] = (string) $arg;
35
+ }
36
+ $visible = call_user_func_array(array($this, $method), $args);
37
+ }
38
+ else {
39
+ $visible = true;
40
+ }
41
+ if ($visible) {
42
+ $method = (string) $actionConfig['action'];
43
+ $this->$method($layout, $name, $actionConfig);
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ /* BASED ON SNIPPET: Models/Event handler */
52
+ /**
53
+ * After blocks are generated change their properties (handles event "controller_action_layout_generate_blocks_after")
54
+ * @param Varien_Event_Observer $observer
55
+ */
56
+ public function postProcessBlocks($observer) {
57
+ /* @var $action Mage_Core_Controller_Varien_Action */ $action = $observer->getEvent()->getAction();
58
+ /* @var $layout Mage_Core_Model_Layout */ $layout = $observer->getEvent()->getLayout();
59
+
60
+ if (Mage::getConfig()->getNode('m_blocks')) {
61
+ foreach (Mage::getConfig()->getNode('m_blocks')->children() as $name => $config) {
62
+ if (in_array($name, $layout->getUpdate()->getHandles())) {
63
+ foreach ($config->children() as $action => $actionConfig) {
64
+ if (isset($actionConfig['if'])) {
65
+ $method = (string) $actionConfig['if'];
66
+ $args = array();
67
+ foreach ($actionConfig->children() as $arg) {
68
+ $args[] = (string) $arg;
69
+ }
70
+ $visible = call_user_func_array(array($this, $method), $args);
71
+ }
72
+ else {
73
+ $visible = true;
74
+ }
75
+ if ($visible) {
76
+ foreach ($this->_findBlocks($layout, $actionConfig) as $block) {
77
+ $method = (string) $actionConfig['action'];
78
+ $this->$method($block, $actionConfig);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+
87
+
88
+ protected function _findBlocks($layout, $actionConfig) {
89
+ $result = array();
90
+
91
+ if (isset($actionConfig['type'])) {
92
+ $value = $block = Mage::getConfig()->getBlockClassName((string) $actionConfig['type']);
93
+ foreach ($layout->getAllBlocks() as $block) {
94
+ if ($block instanceof $value) {
95
+ $result[] = $block;
96
+ }
97
+ }
98
+ }
99
+ else {
100
+ throw new Exception('Not implemented');
101
+ }
102
+
103
+ return $result;
104
+ }
105
+
106
+ // CONDITION METHODS
107
+
108
+ public function flagSet($param) {
109
+ return Mage::getStoreConfigFlag($param);
110
+ }
111
+ public function flagNotSet($param) {
112
+ return ! Mage::getStoreConfigFlag($param);
113
+ }
114
+ public function valueEquals($param, $value) {
115
+ return (Mage::getStoreConfig($param) == $value);
116
+ }
117
+ public function valueNotEquals($param, $value) {
118
+ return (Mage::getStoreConfig($param) != $value);
119
+ }
120
+
121
+ // LAYOUT HANDLE METHODS
122
+
123
+ public function addAfter($layout, $name, $actionConfig) {
124
+ if ($handle = (string)$actionConfig['handle']) {
125
+ $handles = $layout->getUpdate()->getHandles();
126
+ $index = array_search($name, $handles);
127
+ $layout->getUpdate()->resetHandles()->addHandle(array_merge(
128
+ array_slice($handles, 0, $index + 1),
129
+ array($handle),
130
+ $index + 1 < count($handles) ? array_slice($handles, $index + 1) : array()
131
+ ));
132
+ }
133
+ }
134
+
135
+ // BLOCK ACTION METHODS
136
+
137
+ public function setTemplate($block, $actionConfig) {
138
+ $block->setTemplate((string)$actionConfig['template']);
139
+ }
140
+ /**
141
+ * Adds css files to header (handles event "core_block_abstract_to_html_after")
142
+ * @param Varien_Event_Observer $observer
143
+ */
144
+ public function adhocCss($observer) {
145
+ /* @var $block Mage_Core_Block_Abstract */ $block = $observer->getEvent()->getBlock();
146
+ /* @var $transport Varien_Object */ $transport = $observer->getEvent()->getTransport();
147
+
148
+ if ($block->getNameInLayout() == 'head' && ($css = $block->getMCss())) {
149
+ /* @var $files Mana_Core_Helper_Files */ $files = Mage::helper(strtolower('Mana_Core/Files'));
150
+ $html = '';
151
+ foreach ($css as $relativeUrl) {
152
+ if ($files->getFilename($relativeUrl, 'css')) {
153
+ $html .= '<link rel="stylesheet" type="text/css" href="'.$files->getUrl($relativeUrl, 'css').'" />'."\n";
154
+ }
155
+ }
156
+ if ($html) {
157
+ $transport->setHtml($transport->getHtml().$html);
158
+ }
159
+ }
160
+ }
161
+ }
app/code/local/Mana/Core/Model/Source/Abstract.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for source classes used to populate SELECT drop downs with values
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ abstract class Mana_Core_Model_Source_Abstract {
15
+ protected $_options = null;
16
+
17
+ /**
18
+ * Retrieve all options array
19
+ *
20
+ * @return array
21
+ */
22
+ public function getAllOptions()
23
+ {
24
+ if (is_null($this->_options)) {
25
+ $this->_options = $this->_getAllOptions();
26
+ }
27
+ return $this->_options;
28
+ }
29
+
30
+ protected abstract function _getAllOptions();
31
+
32
+ /**
33
+ * Retrieve option array
34
+ *
35
+ * @return array
36
+ */
37
+ public function getOptionArray()
38
+ {
39
+ $_options = array();
40
+ foreach ($this->getAllOptions() as $option) {
41
+ $_options[$option['value']] = $option['label'];
42
+ }
43
+ return $_options;
44
+ }
45
+
46
+ /**
47
+ * Get a text for option value
48
+ *
49
+ * @param string|integer $value
50
+ * @return string
51
+ */
52
+ public function getOptionText($value)
53
+ {
54
+ $options = $this->getAllOptions();
55
+ foreach ($options as $option) {
56
+ if ($option['value'] == $value) {
57
+ return $option['label'];
58
+ }
59
+ }
60
+ return false;
61
+ }
62
+
63
+ public function toOptionArray() {
64
+ return $this->getOptionArray();
65
+ }
66
+ }
app/code/local/Mana/Core/Model/Source/Config.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for source classes used to populate SELECT drop downs with values
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Source_Config extends Mana_Core_Model_Source_Abstract {
15
+ protected $_rootNode;
16
+ protected $_childNode;
17
+ protected $_defaultTranslationModule;
18
+
19
+ protected function _getAllOptions() {
20
+ /* @var $core Mana_Core_Helper_Data */
21
+ $core = Mage::helper(strtolower('Mana_Core'));
22
+ $result = array();
23
+
24
+ foreach ($core->getSortedXmlChildren(Mage::getConfig()->getNode($this->_rootNode), $this->_childNode) as $key => $options) {
25
+ $module = isset($options['module']) ? ((string)$options['module']) : $this->_defaultTranslationModule;
26
+ $result[] = array('label' => Mage::helper($module)->__((string)$options->title), 'value' => $key);
27
+ }
28
+ return $result;
29
+ }
30
+ }
app/code/local/Mana/Core/Model/Source/Country.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Core_Model_Source_Country extends Mana_Core_Model_Source_Abstract {
13
+ protected function _getAllOptions() {
14
+ return Mage::getResourceModel('directory/country_collection')->load()->toOptionArray();
15
+ }
16
+ }
app/code/local/Mana/Core/Model/Source/Yesno.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Source for options of filter being filterable
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Model_Source_Yesno extends Mana_Core_Model_Source_Abstract {
15
+ protected function _getAllOptions() {
16
+ return array(
17
+ array('value' => '1', 'label' => Mage::helper('core')->__('Yes')),
18
+ array('value' => '0', 'label' => Mage::helper('core')->__('No')),
19
+ );
20
+ }
21
+ }
app/code/local/Mana/Core/Profiler.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Profiler {
15
+ public static function start() {
16
+ $args = func_get_args();
17
+ $name = '';
18
+ foreach ($args as $arg) {
19
+ if ($name) $name .= '::';
20
+ $name .= $arg;
21
+ Varien_Profiler::start($name);
22
+ }
23
+ }
24
+ public static function stop() {
25
+ $args = func_get_args();
26
+ $name = '';
27
+ foreach ($args as $arg) {
28
+ if ($name) $name .= '::';
29
+ $name .= $arg;
30
+ Varien_Profiler::stop($name);
31
+ }
32
+ }
33
+ }
app/code/local/Mana/Core/Resource/Attribute/Collection.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Attribute collection with exetnded info
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Resource_Attribute_Collection extends Mage_Eav_Model_Mysql4_Entity_Attribute_Collection
15
+ {
16
+ protected $_entityType;
17
+ public function getEntityType() {
18
+ return $this->_entityType;
19
+ }
20
+ public function setEntityType($value) {
21
+ $this->_entityType = $value;
22
+ return $this;
23
+ }
24
+ protected function _initSelect()
25
+ {
26
+ $this->getSelect()->from(array('main_table' => $this->getResource()->getMainTable()))
27
+ ->where('main_table.entity_type_id=?',
28
+ Mage::getModel('eav/entity')->setType($this->getEntityType())->getTypeId())
29
+ ->join(
30
+ array('additional_table' => $this->getTable('mana_core/attribute')),
31
+ 'additional_table.attribute_id=main_table.attribute_id'
32
+ );
33
+ return $this;
34
+ }
35
+
36
+ /**
37
+ * Specify attribute entity type filter
38
+ *
39
+ * @param int $typeId
40
+ * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Attribute_Collection
41
+ */
42
+ public function setEntityTypeFilter($typeId)
43
+ {
44
+ return $this;
45
+ }
46
+
47
+ public function addIsKeyFilter() {
48
+ $this->getSelect()->where('additional_table.is_key = 1');
49
+ return $this;
50
+ }
51
+ }
app/code/local/Mana/Core/Resource/Eav.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for eav resources which can handle store-scoped values as well as default for global scope.
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Resource_Eav extends Mage_Eav_Model_Entity_Abstract {
15
+ protected function _construct() {
16
+ $this->_read = Mage::getSingleton('core/resource')->getConnection('core_read');
17
+ $this->_write = Mage::getSingleton('core/resource')->getConnection('core_write');
18
+ }
19
+
20
+ public function loadDefaults($model) {
21
+ $this->loadAllAttributes();
22
+ foreach ($this->getAttributesByCode() as $attributeCode => $attribute) {
23
+ if ($attribute->getHasDefault() && $model->isDefaultValue($attribute)
24
+ && (!$model->getStoreId() || !$this->hasStoreValue($model, $attribute)))
25
+ {
26
+ $defaultProvider = Mage::getSingleton($attribute->getDefaultModel());
27
+ $model->setData($attributeCode,
28
+ $defaultProvider->getDefaultValue($model, $attributeCode, $attribute->getDefaultSource()));
29
+ }
30
+ }
31
+ return $this;
32
+ }
33
+
34
+ protected $_keyAttributes;
35
+ public function getKeyAttributes() {
36
+ if (!$this->_keyAttributes) {
37
+ $this->_keyAttributes = Mage::getResourceModel($this->getEntityType()->getEntityAttributeCollection())
38
+ ->addIsKeyFilter();
39
+ }
40
+ return $this->_keyAttributes;
41
+ }
42
+ /**
43
+ * Checks whether entity has attribute value for specific store
44
+ * @param Mana_Core_Model_Eav $model
45
+ * @param Mage_Eav_Model_Entity_Attribute $attribute
46
+ * @param int $storeId
47
+ */
48
+ public function hasStoreValue($model, $attribute) {
49
+ return $this->getStoreValueId($model, $attribute) != false;
50
+ }
51
+ public function getStoreValueId($model, $attribute) {
52
+ if ($model->getId()) {
53
+ return $this->getReadConnection()->fetchOne("SELECT value_id FROM {$attribute->getBackendTable()} WHERE
54
+ (entity_id = {$model->getId()}) AND (attribute_id = {$attribute->getId()}) AND (store_id = {$model->getStoreId()})");
55
+ }
56
+ else {
57
+ return false;
58
+ }
59
+ }
60
+ protected function _saveAttribute($object, $attribute, $value) {
61
+ $table = $attribute->getBackend()->getTable();
62
+ if (!isset($this->_attributeValuesToSave[$table])) {
63
+ $this->_attributeValuesToSave[$table] = array();
64
+ }
65
+
66
+ $entityIdField = $attribute->getBackend()->getEntityIdField();
67
+
68
+ $data = array(
69
+ 'entity_type_id' => $object->getEntityTypeId(),
70
+ $entityIdField => $object->getId(),
71
+ 'attribute_id' => $attribute->getId(),
72
+ 'store_id' => $object->getStoreId(),
73
+ 'value' => $this->_prepareValueForSave($value, $attribute)
74
+ );
75
+
76
+ $this->_attributeValuesToSave[$table][] = $data;
77
+
78
+ return $this;
79
+ }
80
+ protected function _collectSaveData($newObject) {
81
+ // when deleting store specific values, value ids should be in place
82
+ if ($newObject->getStoreId()) {
83
+ foreach ($this->getAttributesByCode() as $code => $attribute) {
84
+ if ($attribute->getIsGlobal() == Mana_Core_Model_Attribute_Scope::_STORE && is_null($newObject->getData($code))) {
85
+ $attribute->getBackend()->setValueId($this->getStoreValueId($newObject, $attribute));
86
+ }
87
+ }
88
+ }
89
+
90
+ return parent::_collectSaveData($newObject);
91
+ }
92
+
93
+ }
app/code/local/Mana/Core/Resource/Eav/Collection.php ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for DB-backed collections
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Resource_Eav_Collection extends Mage_Eav_Model_Entity_Collection_Abstract {
15
+ protected $_storeId = null;
16
+
17
+ public function setStore($store)
18
+ {
19
+ $this->setStoreId(Mage::app()->getStore($store)->getId());
20
+ return $this;
21
+ }
22
+
23
+ public function setStoreId($storeId)
24
+ {
25
+ if ($storeId instanceof Mage_Core_Model_Store) {
26
+ $storeId = $storeId->getId();
27
+ }
28
+ $this->_storeId = $storeId;
29
+ return $this;
30
+ }
31
+
32
+ public function getStoreId()
33
+ {
34
+ if (is_null($this->_storeId)) {
35
+ $this->setStoreId(Mage::app()->getStore()->getId());
36
+ }
37
+ return $this->_storeId;
38
+ }
39
+
40
+ public function addStoreFilter($store=null)
41
+ {
42
+ if (is_null($store)) {
43
+ $store = $this->getStoreId();
44
+ }
45
+ $store = Mage::app()->getStore($store);
46
+
47
+ if (!$store->isAdmin()) {
48
+ $this->setStoreId($store);
49
+ }
50
+
51
+ return $this;
52
+ }
53
+
54
+ public function get($fields) {
55
+ $result = array();
56
+ foreach ($fields as $field) {
57
+ $result[$field] = $this->$field;
58
+ }
59
+ return $result;
60
+ }
61
+
62
+ public function set($values) {
63
+ foreach ($values as $field => $value) {
64
+ $this->$field = $value;
65
+ }
66
+ return $this;
67
+ }
68
+ public function load($printQuery = false, $logQuery = false) {
69
+ if ($this->isLoaded()) {
70
+ return $this;
71
+ }
72
+ parent::load($printQuery, $logQuery);
73
+ if ($this->_items) {
74
+ foreach ($this->_items as $index => $item) {
75
+ if ($this->_isValidItem($item)) {
76
+ $item->setStoreId($this->_storeId);
77
+ $item->loadDefaults();
78
+ }
79
+ else {
80
+ unset($this->_items[$index]);
81
+ }
82
+ }
83
+ }
84
+ return $this;
85
+ }
86
+ protected function _isValidItem($item) {
87
+ return true;
88
+ }
89
+ protected function _getLoadAttributesSelect($table, $attributeIds = array())
90
+ {
91
+ if (empty($attributeIds)) {
92
+ $attributeIds = $this->_selectAttributes;
93
+ }
94
+ if ((int) $this->getStoreId()) {
95
+ $entityIdField = $this->getEntity()->getEntityIdField();
96
+ $joinCondition = 'store.attribute_id=default.attribute_id
97
+ AND store.entity_id=default.entity_id
98
+ AND store.store_id='.(int) $this->getStoreId();
99
+
100
+ $select = $this->getConnection()->select()
101
+ ->from(array('default'=>$table), array($entityIdField, 'attribute_id', 'default_value'=>'value'))
102
+ ->joinLeft(
103
+ array('store'=>$table),
104
+ $joinCondition,
105
+ array(
106
+ 'store_value' => 'value',
107
+ 'value' => new Zend_Db_Expr('IF(store.value_id>0, store.value, default.value)')
108
+ )
109
+ )
110
+ ->where('default.entity_type_id=?', $this->getEntity()->getTypeId())
111
+ ->where("default.$entityIdField in (?)", array_keys($this->_itemsById))
112
+ ->where('default.attribute_id in (?)', $attributeIds)
113
+ ->where('default.store_id = 0');
114
+ }
115
+ else {
116
+ if (empty($attributeIds)) {
117
+ $attributeIds = $this->_selectAttributes;
118
+ }
119
+ $entityIdField = $this->getEntity()->getEntityIdField();
120
+ $select = $this->getConnection()->select()
121
+ ->from($table, array($entityIdField, 'attribute_id', 'value'))
122
+ ->where('entity_type_id =?', $this->getEntity()->getTypeId())
123
+ ->where("$entityIdField IN (?)", array_keys($this->_itemsById))
124
+ ->where('attribute_id IN (?)', $attributeIds)
125
+ ->where('store_id=?', $this->getDefaultStoreId());
126
+ }
127
+ return $select;
128
+ }
129
+ protected function _joinAttributeToSelect($method, $attribute, $tableAlias, $condition, $fieldCode, $fieldAlias)
130
+ {
131
+ if (isset($this->_joinAttributes[$fieldCode]['store_id'])) {
132
+ $store_id = $this->_joinAttributes[$fieldCode]['store_id'];
133
+ }
134
+ else {
135
+ $store_id = $this->getStoreId();
136
+ }
137
+
138
+ if ($store_id != $this->getDefaultStoreId() && $attribute->getIsGlobal() != Mana_Core_Model_Attribute_Scope::_GLOBAL) {
139
+ /**
140
+ * Add joining default value for not default store
141
+ * if value for store is null - we use default value
142
+ */
143
+ $defCondition = '('.join(') AND (', $condition).')';
144
+ $defAlias = $tableAlias.'_default';
145
+ $defFieldCode = $fieldCode.'_default';
146
+ $defFieldAlias= str_replace($tableAlias, $defAlias, $fieldAlias);
147
+
148
+ $defCondition = str_replace($tableAlias, $defAlias, $defCondition);
149
+ $defCondition.= $this->getConnection()->quoteInto(" AND $defAlias.store_id=?", $this->getDefaultStoreId());
150
+
151
+ $this->getSelect()->$method(
152
+ array($defAlias => $attribute->getBackend()->getTable()),
153
+ $defCondition,
154
+ array()
155
+ );
156
+
157
+ $method = 'joinLeft';
158
+ $fieldAlias = new Zend_Db_Expr("IF($tableAlias.value_id>0, $fieldAlias, $defFieldAlias)");
159
+ $this->_joinAttributes[$fieldCode]['condition_alias'] = $fieldAlias;
160
+ $this->_joinAttributes[$fieldCode]['attribute'] = $attribute;
161
+ }
162
+ else {
163
+ $store_id = $this->getDefaultStoreId();
164
+ }
165
+ $condition[] = $this->getConnection()->quoteInto("$tableAlias.store_id=?", $store_id);
166
+ return parent::_joinAttributeToSelect($method, $attribute, $tableAlias, $condition, $fieldCode, $fieldAlias);
167
+ }
168
+ public function getDefaultStoreId()
169
+ {
170
+ return Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID;
171
+ }
172
+
173
+ public function _loadAttributes($printQuery = false, $logQuery = false)
174
+ {
175
+ if (empty($this->_items) || empty($this->_itemsById) || empty($this->_selectAttributes)) {
176
+ return $this;
177
+ }
178
+
179
+ $entity = $this->getEntity();
180
+ $entityIdField = $entity->getEntityIdField();
181
+
182
+ $tableAttributes = array();
183
+ foreach ($this->_selectAttributes as $attributeCode => $attributeId) {
184
+ if (!$attributeId) {
185
+ continue;
186
+ }
187
+ $attribute = Mage::getSingleton('eav/config')->getCollectionAttribute($entity->getType(), $attributeCode);
188
+ if ($attribute && !$attribute->isStatic()) {
189
+ $tableAttributes[$attribute->getBackendTable()][] = $attributeId;
190
+ }
191
+ }
192
+
193
+ $selects = array();
194
+ foreach ($tableAttributes as $table=>$attributes) {
195
+ $selects[] = $this->_getLoadAttributesSelect($table, $attributes);
196
+ }
197
+ if (!empty($selects)) {
198
+ try {
199
+ $select = implode(' UNION ', $selects);
200
+ $values = $this->_fetchAll($select);
201
+ } catch (Exception $e) {
202
+ Mage::printException($e, $select);
203
+ $this->printLogQuery(true, true, $select);
204
+ throw $e;
205
+ }
206
+
207
+ foreach ($values as $value) {
208
+ $this->_setItemAttributeValue($value);
209
+ }
210
+ }
211
+ return $this;
212
+ }
213
+ }
app/code/local/Mana/Core/Resource/Eav/Collection/Derived.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for collection which add additional rows to typical DB-bcaked collections
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Resource_Eav_Collection_Derived extends Mana_Core_Resource_Eav_Collection {
15
+ protected $_allDerivedItems;
16
+ public function getAllDerivedItems() {
17
+ return $this->_allItems;
18
+ }
19
+
20
+ public function load($printQuery = false, $logQuery = false)
21
+ {
22
+ if ($this->isLoaded()) {
23
+ return $this;
24
+ }
25
+
26
+ $this
27
+ ->_loadCustomItems()
28
+ ->_addMissingOriginalItems()
29
+ ->_renderFilters()
30
+ ->_renderOrders();
31
+
32
+ // calculate totals
33
+ $this->_totalRecords = count($this->_items);
34
+ $this->_renderLimit();
35
+ $this->_setIsLoaded();
36
+
37
+ return $this;
38
+ }
39
+
40
+ protected function _loadCustomItems() {
41
+ $collection = substr(get_class($this), 0, strlen(get_class($this)) - strlen('_Derived'));
42
+ /* @var $collection Mana_Core_Resource_Eav_Collection */ $collection = new $collection;
43
+ $collection->set($this->get(array('_filterAttributes', '_filters', '_flags', '_joinAttributes',
44
+ '_joinEntities', '_joinFields', '_orders', '_selectAttributes', '_selectEntityTypes',
45
+ '_storeId', '_select'))); // '_pageSize', '_curPage',
46
+ foreach ($collection as $item) {
47
+ $this->addItem($item);
48
+ }
49
+ return $this;
50
+ }
51
+ protected function _addMissingOriginalItems() {
52
+ return $this;
53
+ }
54
+ protected function _renderFilters() {
55
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
56
+ $items = array();
57
+ foreach ($this->_items as $key => $item) {
58
+ $conforms = true;
59
+ foreach ($this->_mFilters as $filter) {
60
+ if ($filter['attribute'] == 'entity_type_id') continue;
61
+
62
+ $method = 'get'.$core->pascalCased($filter['attribute']);
63
+ $value = $item->$method();
64
+ if (isset($filter['condition']['like'])) {
65
+ $value = mb_convert_case($value, MB_CASE_UPPER, "UTF-8");
66
+ $test = mb_convert_case($filter['condition']['like'], MB_CASE_UPPER, "UTF-8");
67
+ if (mb_strpos($value, mb_substr($test, 1, mb_strlen($test) - 2)) === false) {
68
+ $conforms = false;
69
+ break;
70
+ }
71
+ }
72
+ elseif (isset($filter['condition']['eq'])) {
73
+ $test = $filter['condition']['eq'];
74
+ if ($value != $test) {
75
+ $conforms = false;
76
+ break;
77
+ }
78
+ }
79
+ }
80
+ if ($conforms) {
81
+ $items[$key] = $item;
82
+ }
83
+ }
84
+ $this->_items = $items;
85
+ return $this;
86
+ }
87
+ protected function _renderLimit() {
88
+ if ($this->_pageSize !== false) {
89
+ $items = array();
90
+ $index = 0;
91
+ $from = $this->_pageSize * ($this->_curPage - 1);
92
+ $to = $this->_pageSize * $this->_curPage;
93
+ foreach ($this->_items as $key => $item) {
94
+ if ($from <= $index && $index < $to) {
95
+ $items[$key] = $item;
96
+ }
97
+ $index++;
98
+ }
99
+ $this->_items = $items;
100
+ }
101
+ return $this;
102
+ }
103
+ protected function _renderOrders() {
104
+ if ($this->_mOrder) {
105
+ uasort($this->_items, array($this, '_orderCallback'));
106
+ }
107
+
108
+ return $this;
109
+ }
110
+
111
+ public function _orderCallback($a, $b) {
112
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
113
+ $method = 'get'.$core->pascalCased($this->_mOrder['attribute']);
114
+ $aValue = $a->$method();
115
+ $bValue = $b->$method();
116
+ if ($aValue == $bValue) return 0;
117
+ if (is_string($aValue)) $aValue = mb_convert_case($aValue, MB_CASE_UPPER, "UTF-8");
118
+ if (is_string($bValue)) $bValue = mb_convert_case($bValue, MB_CASE_UPPER, "UTF-8");
119
+ return (strtolower($this->_mOrder['dir']) == 'desc' ? -1 : 1) * (($aValue < $bValue) ? -1 : 1);
120
+ }
121
+ protected $_mOrder;
122
+ public function setOrder($attribute, $dir='desc') {
123
+ parent::setOrder($attribute, $dir);
124
+ $this->_mOrder = array('attribute' => $attribute, 'dir' => $dir);
125
+ return $this;
126
+ }
127
+ protected $_mFilters = array();
128
+ public function addAttributeToFilter($attribute, $condition=null, $joinType='inner') {
129
+ //parent::addAttributeToFilter($attribute, $condition, $joinType);
130
+ $this->_mFilters[] = array('attribute' => $attribute, 'condition' => $condition);
131
+ }
132
+ }
app/code/local/Mana/Core/Resource/Eav/Setup.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for setup scripts
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Resource_Eav_Setup extends Mage_Eav_Model_Entity_Setup {
15
+ protected function _prepareValues($attr)
16
+ {
17
+ $data = parent::_prepareValues($attr);
18
+ $data = array_merge($data, array(
19
+ 'is_key' => $this->_getValue($attr, 'is_key', 0),
20
+ 'is_global' => $this->_getValue($attr, 'is_global', Mana_Core_Model_Attribute_Scope::_GLOBAL),
21
+ 'has_default' => $this->_getValue($attr, 'has_default', 0),
22
+ 'default_model' => $this->_getValue($attr, 'default_model', ''),
23
+ 'default_source' => $this->_getValue($attr, 'default_source', ''),
24
+ 'default_mask' => $this->_getValue($attr, 'default_mask', 0),
25
+ 'default_mask_field' => $this->_getValue($attr, 'default_mask_field', 'default_mask0'),
26
+ ));
27
+ return $data;
28
+ }
29
+ public function updateDefaultMaskFields($entity) {
30
+ /* @var $db Varien_Db_Adapter_Pdo_Mysql */ $db = $this->getConnection();
31
+ $defaultMaskFields = $db->fetchCol("SELECT DISTINCT m.`default_mask_field`
32
+ FROM {$this->getTable('m_attribute')} AS m
33
+ INNER JOIN {$this->getTable('eav_attribute')} AS a ON a.attribute_id = m.attribute_id
34
+ INNER JOIN {$this->getTable('eav_entity_type')} AS t ON t.entity_type_id = a.entity_type_id
35
+ WHERE t.`entity_type_code` = '$entity'");
36
+ $existingFields = $db->describeTable($this->getTable($entity));
37
+ foreach ($defaultMaskFields as $defaultMaskField) {
38
+ if (!isset($existingFields[$defaultMaskField])) {
39
+ $this->run("
40
+ ALTER TABLE `{$this->getTable($entity)}` ADD COLUMN (
41
+ `$defaultMaskField` int(10) unsigned NOT NULL default '0'
42
+ );
43
+ ");
44
+ }
45
+ }
46
+ return $this;
47
+ }
48
+ public function getDefaultEntities() {
49
+ return array();
50
+ }
51
+ public function getEntityExtensions() {
52
+ return array();
53
+ }
54
+ public function installEntities($entities=null) {
55
+ parent::installEntities($entities);
56
+
57
+ foreach ($this->getEntityExtensions() as $entityName=>$entity) {
58
+ $frontendPrefix = isset($entity['frontend_prefix']) ? $entity['frontend_prefix'] : '';
59
+ $backendPrefix = isset($entity['backend_prefix']) ? $entity['backend_prefix'] : '';
60
+ $sourcePrefix = isset($entity['source_prefix']) ? $entity['source_prefix'] : '';
61
+
62
+ foreach ($entity['attributes'] as $attrCode=>$attr) {
63
+ if (!empty($attr['backend'])) {
64
+ if ('_'===$attr['backend']) {
65
+ $attr['backend'] = $backendPrefix;
66
+ } elseif ('_'===$attr['backend']{0}) {
67
+ $attr['backend'] = $backendPrefix.$attr['backend'];
68
+ } else {
69
+ $attr['backend'] = $attr['backend'];
70
+ }
71
+ }
72
+ if (!empty($attr['frontend'])) {
73
+ if ('_'===$attr['frontend']) {
74
+ $attr['frontend'] = $frontendPrefix;
75
+ } elseif ('_'===$attr['frontend']{0}) {
76
+ $attr['frontend'] = $frontendPrefix.$attr['frontend'];
77
+ } else {
78
+ $attr['frontend'] = $attr['frontend'];
79
+ }
80
+ }
81
+ if (!empty($attr['source'])) {
82
+ if ('_'===$attr['source']) {
83
+ $attr['source'] = $sourcePrefix;
84
+ } elseif ('_'===$attr['source']{0}) {
85
+ $attr['source'] = $sourcePrefix.$attr['source'];
86
+ } else {
87
+ $attr['source'] = $attr['source'];
88
+ }
89
+ }
90
+
91
+ $this->addAttribute($entityName, $attrCode, $attr);
92
+ }
93
+ }
94
+
95
+ return $this;
96
+ }
97
+ // no changes here: just 1.6 version of this method is incorrect for TEXT values
98
+ public function createEntityTables($baseName, array $options=array())
99
+ {
100
+ $sql = '';
101
+
102
+ if (empty($options['no-main'])) {
103
+ $sql = "
104
+ DROP TABLE IF EXISTS `{$baseName}`;
105
+ CREATE TABLE `{$baseName}` (
106
+ `entity_id` int(10) unsigned NOT NULL auto_increment,
107
+ `entity_type_id` smallint(8) unsigned NOT NULL default '0',
108
+ `attribute_set_id` smallint(5) unsigned NOT NULL default '0',
109
+ `increment_id` varchar(50) NOT NULL default '',
110
+ `parent_id` int(10) unsigned NULL default '0',
111
+ `store_id` smallint(5) unsigned NOT NULL default '0',
112
+ `created_at` datetime NOT NULL default '0000-00-00 00:00:00',
113
+ `updated_at` datetime NOT NULL default '0000-00-00 00:00:00',
114
+ `is_active` tinyint(1) unsigned NOT NULL default '1',
115
+ PRIMARY KEY (`entity_id`),
116
+ CONSTRAINT `FK_{$baseName}_type` FOREIGN KEY (`entity_type_id`) REFERENCES `{$this->getTable('eav_entity_type')}` (`entity_type_id`) ON DELETE CASCADE ON UPDATE CASCADE,
117
+ CONSTRAINT `FK_{$baseName}_store` FOREIGN KEY (`store_id`) REFERENCES `{$this->getTable('core_store')}` (`store_id`) ON DELETE CASCADE ON UPDATE CASCADE
118
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
119
+ }
120
+
121
+ $types = array(
122
+ 'datetime'=>'datetime',
123
+ 'decimal'=>'decimal(12,4)',
124
+ 'int'=>'int',
125
+ 'text'=>'text',
126
+ 'varchar'=>'varchar(255)',
127
+ );
128
+ if (!empty($options['types']) && is_array($options['types'])) {
129
+ if ($options['no-default-types']) {
130
+ $types = array();
131
+ }
132
+ $types = array_merge($types, $options['types']);
133
+ }
134
+
135
+ foreach ($types as $type=>$fieldType) {
136
+ $sql .= "
137
+ DROP TABLE IF EXISTS `{$baseName}_{$type}`;
138
+ CREATE TABLE `{$baseName}_{$type}` (
139
+ `value_id` int(11) NOT NULL auto_increment,
140
+ `entity_type_id` smallint(8) unsigned NOT NULL default '0',
141
+ `attribute_id` smallint(5) unsigned NOT NULL default '0',
142
+ `store_id` smallint(5) unsigned NOT NULL default '0',
143
+ `entity_id` int(10) unsigned NOT NULL default '0',
144
+ `value` {$fieldType} NOT NULL,
145
+ PRIMARY KEY (`value_id`),
146
+ UNIQUE KEY `IDX_BASE` (`entity_type_id`,`entity_id`,`attribute_id`,`store_id`),
147
+ ".($type!=='text' ? "
148
+ KEY `value_by_attribute` (`attribute_id`,`value`),
149
+ KEY `value_by_entity_type` (`entity_type_id`,`value`),
150
+ " : "")."
151
+ CONSTRAINT `FK_{$baseName}_{$type}` FOREIGN KEY (`entity_id`) REFERENCES `{$baseName}` (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE,
152
+ CONSTRAINT `FK_{$baseName}_{$type}_attribute` FOREIGN KEY (`attribute_id`) REFERENCES `{$this->getTable('eav_attribute')}` (`attribute_id`) ON DELETE CASCADE ON UPDATE CASCADE,
153
+ CONSTRAINT `FK_{$baseName}_{$type}_entity_type` FOREIGN KEY (`entity_type_id`) REFERENCES `{$this->getTable('eav_entity_type')}` (`entity_type_id`) ON DELETE CASCADE ON UPDATE CASCADE,
154
+ CONSTRAINT `FK_{$baseName}_{$type}_store` FOREIGN KEY (`store_id`) REFERENCES `{$this->getTable('core_store')}` (`store_id`) ON DELETE CASCADE ON UPDATE CASCADE
155
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
156
+ }
157
+
158
+ try {
159
+ $this->_conn->multi_query($sql);
160
+ } catch (Exception $e) {
161
+ throw $e;
162
+ }
163
+
164
+ return $this;
165
+ }
166
+ }
app/code/local/Mana/Core/Resource/Virtual/Collection.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for virtual collections (not based on SELECT SQL statement). All items must be Varien_Object derived
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Core_Resource_Virtual_Collection extends Varien_Data_Collection {
15
+ public function load($printQuery = false, $logQuery = false)
16
+ {
17
+ if ($this->isLoaded()) {
18
+ return $this;
19
+ }
20
+
21
+ $this
22
+ ->_loadCustomItems()
23
+ ->_addMissingOriginalItems()
24
+ ->_renderFilters()
25
+ ->_renderOrders();
26
+
27
+ // calculate totals
28
+ $this->_totalRecords = count($this->_items);
29
+ $this->_renderLimit();
30
+ $this->_setIsLoaded();
31
+
32
+ return $this;
33
+ }
34
+
35
+ protected function _loadCustomItems() {
36
+ return $this;
37
+ }
38
+ protected function _addMissingOriginalItems() {
39
+ return $this;
40
+ }
41
+ protected function _renderFilters() {
42
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
43
+ $items = array();
44
+ foreach ($this->_items as $key => $item) {
45
+ $conforms = true;
46
+ foreach ($this->_mFilters as $filter) {
47
+ if ($filter['attribute'] == 'entity_type_id') continue;
48
+
49
+ $method = 'get'.$core->pascalCased($filter['attribute']);
50
+ $value = $item->$method();
51
+ if (isset($filter['condition']['like'])) {
52
+ $value = mb_convert_case($value, MB_CASE_UPPER, "UTF-8");
53
+ $test = mb_convert_case($filter['condition']['like'], MB_CASE_UPPER, "UTF-8");
54
+ if (mb_strpos($value, mb_substr($test, 1, mb_strlen($test) - 2)) === false) {
55
+ $conforms = false;
56
+ break;
57
+ }
58
+ }
59
+ elseif (isset($filter['condition']['eq'])) {
60
+ $test = $filter['condition']['eq'];
61
+ if ($value != $test) {
62
+ $conforms = false;
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ if ($conforms) {
68
+ $items[$key] = $item;
69
+ }
70
+ }
71
+ $this->_items = $items;
72
+ return $this;
73
+ }
74
+ protected function _renderLimit() {
75
+ if ($this->_pageSize !== false) {
76
+ $items = array();
77
+ $index = 0;
78
+ $from = $this->_pageSize * ($this->_curPage - 1);
79
+ $to = $this->_pageSize * $this->_curPage;
80
+ foreach ($this->_items as $key => $item) {
81
+ if ($from <= $index && $index < $to) {
82
+ $items[$key] = $item;
83
+ }
84
+ $index++;
85
+ }
86
+ $this->_items = $items;
87
+ }
88
+ return $this;
89
+ }
90
+ protected function _renderOrders() {
91
+ if ($this->_mOrder) {
92
+ uasort($this->_items, array($this, '_orderCallback'));
93
+ }
94
+
95
+ return $this;
96
+ }
97
+
98
+ public function _orderCallback($a, $b) {
99
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
100
+ $method = 'get'.$core->pascalCased($this->_mOrder['attribute']);
101
+ $aValue = $a->$method();
102
+ $bValue = $b->$method();
103
+ if ($aValue == $bValue) return 0;
104
+ if (is_string($aValue)) $aValue = mb_convert_case($aValue, MB_CASE_UPPER, "UTF-8");
105
+ if (is_string($bValue)) $bValue = mb_convert_case($bValue, MB_CASE_UPPER, "UTF-8");
106
+ return (strtolower($this->_mOrder['dir']) == 'desc' ? -1 : 1) * (($aValue < $bValue) ? -1 : 1);
107
+ }
108
+ protected $_mOrder;
109
+ public function setOrder($attribute, $dir='desc') {
110
+ parent::setOrder($attribute, $dir);
111
+ $this->_mOrder = array('attribute' => $attribute, 'dir' => $dir);
112
+ return $this;
113
+ }
114
+ protected $_mFilters = array();
115
+ public function addAttributeToFilter($attribute, $condition=null, $joinType='inner') {
116
+ $this->_mFilters[] = array('attribute' => $attribute, 'condition' => $condition);
117
+ }
118
+ public function addFieldToFilter($attribute, $condition=null) {
119
+ $this->_mFilters[] = array('attribute' => $attribute, 'condition' => $condition);
120
+ }
121
+ }
app/code/local/Mana/Core/etc/config.xml ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ @category Mana
4
+ @package Mana_Core
5
+ @copyright Copyright (c) http://www.manadev.com
6
+ @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ -->
8
+ <!-- BASED ON SNIPPET: New Module/etc/config.xml -->
9
+ <config>
10
+ <!-- This section registers module with Magento system. -->
11
+ <modules>
12
+ <Mana_Core>
13
+ <!-- This version number identifies version of database tables specific to this extension. It is written to
14
+ core_resource table. -->
15
+ <version>12.04.21.23</version>
16
+ </Mana_Core>
17
+ </modules>
18
+ <!-- This section contains module settings which are merged into global configuration during each page load,
19
+ each ajax request. -->
20
+ <global>
21
+ <!-- This section registers helper classes to be accessible through Mage::helper() method. Mana_Core_Helper_Data
22
+ class is accessible through Mage::helper('mana_core') call, other Mana_Core_Helper_XXX_YYY classes are accessible
23
+ through Mage::helper('mana_core/xxx_yyy') call. -->
24
+ <helpers>
25
+ <mana_core>
26
+ <!-- This says that string 'mana_core' corresponds to Mana_Core_Helper pseudo-namespace in
27
+ Mage::helper() calls. -->
28
+ <class>Mana_Core_Helper</class>
29
+ </mana_core>
30
+ </helpers>
31
+ <!-- BASED ON SNIPPET: Blocks/Block support (config.xml) -->
32
+ <!-- This section registers block classes to be accessible from layout XML files (in type="<block type>") or
33
+ through calls to $this->getLayout()->createBlock('<block type>') in block or controller code. That is,
34
+ Mana_Core_Block_XXX_YYY classes are accessible as 'mana_core/xxx_yyy' type strings both in layout files
35
+ and in createBlock() calls. -->
36
+ <blocks>
37
+ <!-- This says that string 'mana_core' corresponds to Mana_Core_Block pseudo-namespace in
38
+ layout xml files and in createBlock() calls. -->
39
+ <mana_core>
40
+ <class>Mana_Core_Block</class>
41
+ </mana_core>
42
+ </blocks>
43
+ <!-- BASED ON SNIPPET: Models/Model support (config.xml) -->
44
+ <!-- This section registers model classes to be accessible through Mage::getModel('<model type>') and through
45
+ Mage::getSingleton('<model type>') calls. That is, Mana_Core_Model_XXX_YYY classes are accessible as
46
+ 'mana_core/xxx_yyy' type strings both in getModel() and getSingleton() calls. -->
47
+ <models>
48
+ <!-- This says that string 'mana_core' corresponds to Mana_Core_Model pseudo-namespace in
49
+ getModel() and getSingleton() calls. -->
50
+ <mana_core>
51
+ <class>Mana_Core_Model</class>
52
+ <!-- BASED ON SNIPPET: Resources/Declare resource section (config.xml) -->
53
+ <!-- This tells Magento to read config/global/models/mana_core_resources sections and register
54
+ resource model information from there -->
55
+ <resourceModel>mana_core_resources</resourceModel>
56
+ <!-- INSERT HERE: resource section name -->
57
+ </mana_core>
58
+ <!-- BASED ON SNIPPET: Resources/Resource support (config.xml) -->
59
+ <!-- This says that string 'mana_core' corresponds to Mana_Core_Resource pseudo-namespace in
60
+ getResourceModel() calls. -->
61
+ <mana_core_resources>
62
+ <class>Mana_Core_Resource</class>
63
+ <entities>
64
+ <attribute><table>m_attribute</table></attribute>
65
+ <!-- INSERT HERE: table-entity mappings -->
66
+ </entities>
67
+ </mana_core_resources>
68
+ <!-- INSERT HERE: rewrites, ... -->
69
+ </models>
70
+ <!-- BASED ON SNIPPET: New Models/Event support (config.xml) -->
71
+ <!-- This section registers event handlers of this module defined in Mana_Core_Model_Observer with events defined in other
72
+ module throughout the system. So when some code in other module invokes an event mentioned in this section, handler
73
+ method of Mana_Core_Model_Observer class gets called. -->
74
+ <events>
75
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
76
+ <controller_action_layout_load_before><!-- this is event name this module listens for -->
77
+ <observers>
78
+ <mana_core>
79
+ <class>mana_core/observer</class> <!-- model name of class containing event handler methods -->
80
+ <method>addLayoutHandles</method> <!-- event handler method name -->
81
+ </mana_core>
82
+ </observers>
83
+ </controller_action_layout_load_before>
84
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
85
+ <controller_action_layout_generate_blocks_after><!-- this is event name this module listens for -->
86
+ <observers>
87
+ <mana_core>
88
+ <class>mana_core/observer</class> <!-- model name of class containing event handler methods -->
89
+ <method>postProcessBlocks</method> <!-- event handler method name -->
90
+ </mana_core>
91
+ </observers>
92
+ </controller_action_layout_generate_blocks_after>
93
+ <core_block_abstract_to_html_after><!-- this is event name this module listens for -->
94
+ <observers>
95
+ <mana_core>
96
+ <class>mana_core/observer</class>
97
+ <!-- model name of class containing event handler methods -->
98
+ <method>adhocCss</method>
99
+ <!-- event handler method name -->
100
+ </mana_core>
101
+ </observers>
102
+ </core_block_abstract_to_html_after>
103
+ </events>
104
+ <!-- BASED ON SNIPPET: Resources/Installation script support (config.xml) -->
105
+ <!-- This tells Magento to analyze sql/mana_core_setup directory for install/upgrade scripts.
106
+ Installation scripts should be named as 'mysql4-install-<new version>.php'.
107
+ Upgrade scripts should be named as mysql4-upgrade-<current version>-<new version>.php -->
108
+ <resources>
109
+ <mana_core_setup>
110
+ <setup>
111
+ <module>Mana_Core</module>
112
+ </setup>
113
+ </mana_core_setup>
114
+ </resources>
115
+ <!-- INSERT HERE: blocks, models, ... -->
116
+ </global>
117
+ <!-- BASED ON SNIPPET: Static Visuals/Frontend section (config.xml) -->
118
+ <!-- This section enables static visual changes in store frontend. -->
119
+ <frontend>
120
+ <!-- BASED ON SNIPPET: Static Visuals/Layout file support (config.xml) -->
121
+ <!-- This section registers additional layout XML file with our module-specific layout changes to be loaded
122
+ and executes during page rendering. -->
123
+ <layout>
124
+ <updates>
125
+ <mana_core>
126
+ <file>mana_core.xml</file>
127
+ </mana_core>
128
+ </updates>
129
+ </layout>
130
+ <translate>
131
+ <modules>
132
+ <Mana_Core>
133
+ <files>
134
+ <default>Mana_Core.csv</default>
135
+ </files>
136
+ </Mana_Core>
137
+ </modules>
138
+ </translate>
139
+
140
+ <!-- INSERT HERE: layout, translate, routers -->
141
+ </frontend>
142
+ <!-- BASED ON SNIPPET: Static Visuals/Adminhtml section (config.xml) -->
143
+ <!-- This section enables static visual changes in admin area. -->
144
+ <adminhtml>
145
+ <!-- BASED ON SNIPPET: Translation support/Adminhtml (config.xml) -->
146
+ <!-- This section registers additional translation file with our module-specific strings to be loaded
147
+ during admin area request processing -->
148
+ <translate>
149
+ <modules>
150
+ <Mana_Core>
151
+ <files>
152
+ <default>Mana_Core.csv</default>
153
+ </files>
154
+ </Mana_Core>
155
+ </modules>
156
+ </translate>
157
+ <!-- BASED ON SNIPPET: Static Visuals/Layout file support (config.xml) -->
158
+ <!-- This section registers additional layout XML file with our module-specific layout changes to be loaded
159
+ and executes during page rendering. -->
160
+ <layout>
161
+ <updates>
162
+ <mana_core>
163
+ <file>mana_core.xml</file>
164
+ </mana_core>
165
+ </updates>
166
+ </layout>
167
+ <!-- INSERT HERE: layout, translate, routers -->
168
+ </adminhtml>
169
+ <!-- INSERT HERE: adminhtml, frontend, ... -->
170
+ <default>
171
+ <mana_dev>
172
+ <debug>
173
+ <jquery_min>1</jquery_min>
174
+ </debug>
175
+ </mana_dev>
176
+ </default>
177
+ </config>
app/code/local/Mana/Core/sql/mana_core_setup/mysql4-install-1.1.1.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
9
+ if (defined('COMPILER_INCLUDE_PATH')) {
10
+ throw new Exception(Mage::helper('mana_core')->__('This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.'));
11
+ }
12
+ /* @var $installer Mage_Core_Model_Resource_Setup */
13
+ $installer = $this;
14
+
15
+ $installer->startSetup();
16
+
17
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
18
+ $table = 'm_attribute';
19
+ $installer->run("
20
+ DROP TABLE IF EXISTS `{$this->getTable($table)}`;
21
+ CREATE TABLE `{$this->getTable($table)}` (
22
+ `attribute_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
23
+ `is_key` tinyint(1) unsigned NOT NULL DEFAULT '0',
24
+ `is_global` tinyint(1) unsigned NOT NULL DEFAULT '1',
25
+ `has_default` tinyint(1) unsigned NOT NULL DEFAULT '0',
26
+ `default_model` varchar(255) NOT NULL DEFAULT '',
27
+ `default_source` varchar(255) NOT NULL DEFAULT '',
28
+ `default_mask_field` varchar(255) NOT NULL DEFAULT '',
29
+ `default_mask` int(11) NOT NULL DEFAULT '0',
30
+
31
+ PRIMARY KEY (`attribute_id`)
32
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
33
+
34
+ ");
35
+
36
+ $installer->endSetup();
app/code/local/Mana/Db/Exception/Validation.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Mana_Db_Exception_Validation extends Exception {
4
+ protected $_errors;
5
+ public function __construct($errors) {
6
+ $this->_errors = $errors;
7
+ }
8
+ public function getErrors() {
9
+ return $this->_errors;
10
+ }
11
+ }
app/code/local/Mana/Db/Helper/Data.php ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* BASED ON SNIPPET: New Module/Helper/Data.php */
9
+ /**
10
+ * Generic helper functions for Mana_Db module. This class is a must for any module even if empty.
11
+ * @author Mana Team
12
+ */
13
+ class Mana_Db_Helper_Data extends Mage_Core_Helper_Abstract {
14
+ protected $_logQueries = true;
15
+ public function getLogQueries() {
16
+ return $this->_logQueries;
17
+ }
18
+ public function logQuery($action, $query) {
19
+ if ($this->getLogQueries()) {
20
+ Mage::log($action.': '.(string)$query, Zend_Log::DEBUG, 'replicate.log');
21
+ }
22
+ }
23
+ public function logAction($action, $object) {
24
+ if ($this->getLogQueries()) {
25
+ Mage::log($action.': '.$object->toJson(), Zend_Log::DEBUG, 'replicate.log');
26
+ }
27
+ }
28
+ public function hasOverriddenValue($object, $values, $bit) {
29
+ $field = 'default_mask'.$this->getMaskIndex($bit);
30
+ if ($object->hasData($field)) {
31
+ return ($object->getData($field) & $this->getMask($bit)) != 0;
32
+ }
33
+ else {
34
+ return ($values[$field] & $this->getMask($bit)) != 0;
35
+ }
36
+ }
37
+ public function hasOverriddenValueEx($object, $bit, $field = 'default_mask') {
38
+ $field .= $this->getMaskIndex($bit);
39
+ return ($object->getData($field) & $this->getMask($bit)) != 0;
40
+ }
41
+ public function getGlobalEntityName($entityName) {
42
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
43
+ if (!$core->endsWith($entityName, '_store')) {
44
+ throw new Exception(Mage::helper('mana_db')->__('Store-level entity %s name should end with _store', $entityName));
45
+ }
46
+ return substr($entityName, 0, strlen($entityName) - strlen('_store'));
47
+ }
48
+ public function joinLeft($select, $alias, $table, $condition) {
49
+ if (!array_key_exists($alias, $select->getPart(Zend_Db_Select::FROM))) {
50
+ $select->joinLeft(array($alias => $table), $condition, null);
51
+ }
52
+ return $select;
53
+ }
54
+ public function getMaskIndex($bit) {
55
+ return ((int)floor($bit / 32));
56
+ }
57
+ public function getMask($bit) {
58
+ return 1 << ($bit % 32);
59
+ }
60
+ public function updateDefaultableField($model, $fieldName, $bit, $fields, $useDefault) {
61
+ if ($useDefault && in_array($fieldName, $useDefault)) {
62
+ $model->setData('default_mask'.$this->getMaskIndex($bit),
63
+ $model->getData('default_mask'.$this->getMaskIndex($bit)) & ~ $this->getMask($bit));
64
+ }
65
+ elseif ($fields && isset($fields[$fieldName])) {
66
+ $model->setData('default_mask'.$this->getMaskIndex($bit),
67
+ $model->getData('default_mask'.$this->getMaskIndex($bit)) | $this->getMask($bit));
68
+ $model->setData($fieldName, $fields[$fieldName]);
69
+ }
70
+ }
71
+ /**
72
+ * Enter description here ...
73
+ * @param Varien_Db_Select $select
74
+ * @param string $column
75
+ */
76
+ public function selectColumn($select, $column) {
77
+ list($expr, $alias) = explode(' AS ', $column);
78
+ foreach ($select->getPart(Zend_Db_Select::COLUMNS) as $columnInfo) {
79
+ if ($columnInfo[2] == $alias) {
80
+ return $this;
81
+ }
82
+ }
83
+ $select->columns($column);
84
+ return $this;
85
+ }
86
+
87
+ public function isReplicatedConfigChanged($config_data) {
88
+ $result = new Varien_Object();
89
+ Mage::dispatchEvent('m_db_is_config_changed', compact('result', 'config_data'));
90
+ return $result->getIsChanged();
91
+ }
92
+
93
+ public function replicateObject($object, $filter) {
94
+ $this->replicate(array(
95
+ 'object' => $object,
96
+ 'filter' => $filter,
97
+ 'trackKeys' => true,
98
+ 'transaction' => false,
99
+ ));
100
+ }
101
+ public function replicate($options = array()) {
102
+ $options = array_merge(array(
103
+ 'db' => Mage::getSingleton('core/resource')->getConnection('core/write'),
104
+ 'trackKeys' => false,
105
+ 'transaction' => true,
106
+ 'filter' => array(),
107
+ 'batchSize' => 10000,
108
+ 'object' => null,
109
+ ), $options);
110
+ if (count($options['filter']) == 0) {
111
+ $options['db']->resetDdlCache();
112
+ }
113
+ if ($options['transaction']) {
114
+ $options['db']->beginTransaction();
115
+ }
116
+ try {
117
+ $options['targets'] = $this->_getAllReplicationTargets();
118
+ foreach ($options['targets'] as $entityName => /* @var $target Mana_Db_Model_Replication_Target */ $target) {
119
+ // assign saved/deleted keys to track
120
+ if ($options['trackKeys']) {
121
+ if (isset($options['filter'][$entityName])) {
122
+ if (isset($options['filter'][$entityName]['saved'])) {
123
+ foreach ($options['filter'][$entityName]['saved'] as $key) {
124
+ $target->setSavedKey($key, $key);
125
+ }
126
+ }
127
+ if (isset($options['filter'][$entityName]['deleted'])) {
128
+ foreach ($options['filter'][$entityName]['deleted'] as $key) {
129
+ $target->setDeletedKey($key, $key);
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ if ($target->getReplicable() && (!$options['object'] || $options['object']->getEntityName() == $entityName)) {
136
+ $model = Mage::getResourceSingleton($entityName);
137
+
138
+ // update existing rows
139
+ $target->setSelects(array())->setIsKeyFilterApplied(false);
140
+ $model->prepareReplicationUpdateSelects($target, $options);
141
+ if (count($target->getSelects()) && (!$options['trackKeys'] || $target->getIsKeyFilterApplied())) {
142
+ foreach ($target->getSelects() as $select) {
143
+ $offset = 0;
144
+ $this->logQuery('UPDATE', $select);
145
+ do {
146
+ $sourceData = $options['db']->fetchAll($select->limit($options['batchSize'], $offset));
147
+ if ($sourceData && count($sourceData)) {
148
+ foreach ($sourceData as $values) {
149
+ if ($object = $model->processReplicationUpdate($values, $options)) {
150
+ if ($object != $options['object']) {
151
+ $this->logAction('UPDATE', $object);
152
+ $object->save();
153
+ if ($options['trackKeys']) {
154
+ $target->setSavedKey($object->getId(), $object->getId());
155
+ }
156
+ }
157
+ else {
158
+ $object->unsetData('_m_prevent_replication');
159
+ }
160
+ }
161
+ }
162
+ }
163
+ $offset += $options['batchSize'];
164
+ } while ($sourceData && count($sourceData));
165
+ }
166
+ }
167
+
168
+ // insert rows
169
+ $target->setSelects(array())->setIsKeyFilterApplied(false);
170
+ $model->prepareReplicationInsertSelects($target, $options);
171
+ if (count($target->getSelects()) && (!$options['trackKeys'] || $target->getIsKeyFilterApplied())) {
172
+ foreach ($target->getSelects() as $select) {
173
+ $offset = 0;
174
+ $this->logQuery('INSERT', $select);
175
+ do {
176
+ $sourceData = $options['db']->fetchAll($select->limit($options['batchSize']));
177
+ if ($sourceData && count($sourceData)) {
178
+ foreach ($sourceData as $values) {
179
+ if ($object = $model->processReplicationInsert($values, $options)) {
180
+ if ($object != $options['object']) {
181
+ $this->logAction('INSERT', $object);
182
+ $object->save();
183
+ if ($options['trackKeys']) {
184
+ $target->setSavedKey($object->getId(), $object->getId());
185
+ }
186
+ }
187
+ else {
188
+ $object->unsetData('_m_prevent_replication');
189
+ }
190
+ }
191
+ }
192
+ }
193
+ $offset += $options['batchSize'];
194
+ } while ($sourceData && count($sourceData));
195
+ }
196
+ }
197
+
198
+ // delete rows
199
+ $target->setSelects(array())->setIsKeyFilterApplied(false);
200
+ $model->prepareReplicationDeleteSelects($target, $options);
201
+ if (count($target->getSelects()) && (!$options['trackKeys'] || $target->getIsKeyFilterApplied())) {
202
+ foreach ($target->getSelects() as $select) {
203
+ $offset = 0;
204
+ $this->logQuery('DELETE', $select);
205
+ do {
206
+ $ids = $options['db']->fetchCol($select->limit($options['batchSize']));
207
+ if ($ids && count($ids)) {
208
+ $model->processReplicationDelete($ids, $options);
209
+ if ($options['trackKeys']) {
210
+ foreach ($ids as $id) {
211
+ $target->setDeletedKey($id, $id);
212
+ }
213
+ }
214
+ }
215
+ $offset += $options['batchSize'];
216
+ } while ($ids && count($ids));
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ if ($options['transaction']) {
223
+ $options['db']->commit();
224
+ }
225
+ }
226
+ catch (Exception $e) {
227
+ if ($options['transaction']) {
228
+ $options['db']->rollback();
229
+ }
230
+ throw $e;
231
+ }
232
+ return $this;
233
+ }
234
+
235
+ /**
236
+ * Enter description here ...
237
+ * @param Varien_Object $result
238
+ * @param Mage_Core_Model_Config_Data $configData
239
+ * @param array $paths
240
+ */
241
+ public function checkIfPathsChanged($result, $configData, $paths) {
242
+ $storeId = $configData->getStoreCode() ? Mage::app()->getStore($configData->getStoreCode())->getId() : 0;
243
+ $this->_changingValues[$storeId.'/'.$configData->getPath()] = $configData->getValue();
244
+ if (!$result->getIsChanged() && in_array($configData->getPath(), $paths) &&
245
+ Mage::getStoreConfig($configData->getPath(), $storeId) != $configData->getValue())
246
+ {
247
+ $result->setIsChanged(true);
248
+ }
249
+ }
250
+ protected $_changingValues = array();
251
+ public function getLatestConfig($path, $storeId = 0) {
252
+ return isset($this->_changingValues[$storeId.'/'.$path])
253
+ ? $this->_changingValues[$storeId.'/'.$path]
254
+ : Mage::getStoreConfig($path, $storeId);
255
+ }
256
+ protected function _getAllReplicationTargets() {
257
+ $result = array();
258
+ foreach (Mage::getConfig()->getNode()->global->models->children() as $module) {
259
+ if (isset($module->resourceModel)) {
260
+ $resourceModel = (string)$module->resourceModel;
261
+ if (isset(Mage::getConfig()->getNode()->global->models->$resourceModel->entities)) {
262
+ foreach (Mage::getConfig()->getNode()->global->models->$resourceModel->entities->children() as $entity) {
263
+ if (!empty($entity->replicable)) {
264
+ $model = $module->getName().'/'.$entity->getName();
265
+ if (!isset($result[$model])) {
266
+ $result[$model] = Mage::getModel('mana_db/replication_target')->setEntityName($model);
267
+ }
268
+ $result[$model]->setReplicable(true);
269
+ foreach (Mage::getResourceSingleton($model)->getReplicationSources() as $source) {
270
+ if (!$result[$model]->hasSourceEntityName($source)) {
271
+ $result[$model]->setSourceEntityName($source, $source);
272
+ }
273
+ if (!isset($result[$source])) {
274
+ $result[$source] = Mage::getModel('mana_db/replication_target')->setEntityName($source);
275
+ }
276
+
277
+ }
278
+ }
279
+ }
280
+ }
281
+ }
282
+ }
283
+
284
+ $count = count($result);
285
+ $orders = array();
286
+ for ($position = 0; $position < $count; $position++) {
287
+ $found = false;
288
+ foreach ($result as $targetName => $target) {
289
+ if (!isset($orders[$targetName])) {
290
+ $hasUnresolvedDependency = false;
291
+ foreach ($target->getSourceEntityNames() as $dependency) {
292
+ if (!isset($orders[$dependency])) {
293
+ // $dependency not yet sorted so $module should wait until that happens
294
+ $hasUnresolvedDependency = true;
295
+ break;
296
+ }
297
+ }
298
+ if (!$hasUnresolvedDependency) {
299
+ $found = $targetName;
300
+ break;
301
+ }
302
+ }
303
+ }
304
+ if ($found) {
305
+ $orders[$found] = count($orders);
306
+ }
307
+ else {
308
+ $circular = array();
309
+ foreach ($result as $targetName => $target) {
310
+ if (!isset($orders[$targetName])) {
311
+ $circular[] = $targetName;
312
+ }
313
+ }
314
+ throw new Exception(Mage::helper('mana_db')->__('Entities with circular dependencies found: %s', implode(', ', $circular)));
315
+ }
316
+ }
317
+ $this->_orders = $orders;
318
+ uasort($result, array($this, '_sortReplicationTargetCallback'));
319
+
320
+ return $result;
321
+ }
322
+ protected $_orders;
323
+ public function _sortReplicationTargetCallback($a, $b) {
324
+ $a = $this->_orders[$a->getEntityName()];
325
+ $b = $this->_orders[$b->getEntityName()];
326
+ if ($a == $b) return 0;
327
+ return $a < $b ? -1 : 1;
328
+ }
329
+
330
+ protected $_inEditing = false;
331
+ public function getInEditing() {
332
+ return $this->_inEditing;
333
+ }
334
+ public function setInEditing($value = true) {
335
+ $this->_inEditing = $value;
336
+ return $this;
337
+ }
338
+ public function beginEditing() {
339
+ return Mage::getResourceSingleton('mana_db/edit_session')->begin();
340
+ }
341
+ }
app/code/local/Mana/Db/Model/Indexer.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://www.manadev.com/license Proprietary License
7
+ */
8
+
9
+ /**
10
+ * Entry points for cron and index processes
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Db_Model_Indexer extends Mage_Index_Model_Indexer_Abstract {
15
+ // INDEXING ITSELF
16
+
17
+ protected function _construct()
18
+ {
19
+ $this->_init('mana_db/replicate');
20
+ }
21
+ public function getName()
22
+ {
23
+ return Mage::helper('mana_db')->__('Default Values');
24
+ }
25
+ public function getDescription()
26
+ {
27
+ return Mage::helper('mana_db')->__('Propagate default values throughout the system');
28
+ }
29
+ protected function _registerEvent(Mage_Index_Model_Event $event)
30
+ {
31
+ }
32
+ protected function _processEvent(Mage_Index_Model_Event $event)
33
+ {
34
+ }
35
+ public function reindexAll() {
36
+ Mage::helper('mana_db')->replicate();
37
+ }
38
+
39
+ public function runCronjob()
40
+ {
41
+ $this->reindexAll();
42
+ }
43
+ }
app/code/local/Mana/Db/Model/Object.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Db_Model_Object extends Mage_Core_Model_Abstract {
15
+ public function getEntityName() {
16
+ return $this->getResourceName();
17
+ }
18
+ public function loadByGlobalId($globalId, $storeId) {
19
+ $this->_getResource()->loadByGlobalId($this, $globalId, $storeId);
20
+ $this->_afterLoad();
21
+ $this->setOrigData();
22
+ $this->_hasDataChanges = false;
23
+ return $this;
24
+ }
25
+
26
+ public function afterCommitCallback() {
27
+ parent::afterCommitCallback();
28
+ if (!$this->getdata('_m_prevent_replication')) {
29
+ Mage::helper('mana_db')->replicate(array(
30
+ 'trackKeys' => true,
31
+ 'filter' => array($this->getResourceName() => array('saved' => array($this->getId()))),
32
+ ));
33
+ }
34
+ }
35
+ protected function _afterDeleteCommit() {
36
+ parent::_afterDeleteCommit();
37
+ if (!$this->getdata('_m_prevent_replication')) {
38
+ Mage::helper('mana_db')->replicate(array(
39
+ 'trackKeys' => true,
40
+ 'filter' => array($this->getResourceName() => array('deleted' => array($this->getId()))),
41
+ ));
42
+ }
43
+ }
44
+ public function addEditedData($fields, $useDefault) {
45
+ $this->getResource()->addEditedData($this, $fields, $useDefault);
46
+ }
47
+ public function addEditedDetails($request) {
48
+ $this->getResource()->addEditedDetails($this, $request);
49
+ }
50
+ public function validateKeys() {
51
+ $result = Mage::getModel('mana_db/validation');
52
+ $this->_validateKeys($result);
53
+ Mage::dispatchEvent('m_db_validateKeys', array('object' => $this, 'result' => $result));
54
+ if (count($result->getErrors())) {
55
+ throw new Mana_Db_Exception_Validation($result->getErrors());
56
+ }
57
+ }
58
+ public function validate() {
59
+ $result = Mage::getModel('mana_db/validation');
60
+ $this->_validate($result);
61
+ Mage::dispatchEvent('m_db_validate', array('object' => $this, 'result' => $result));
62
+ if (count($result->getErrors())) {
63
+ throw new Mana_Db_Exception_Validation($result->getErrors());
64
+ }
65
+ }
66
+ protected function _validateKeys($result) {
67
+ }
68
+ protected function _validate($result) {
69
+ }
70
+ public function assignDefaultValues() {
71
+ $this->_assignDefaultValues();
72
+ Mage::dispatchEvent('m_db_assign_defaults', array('object' => $this));
73
+ }
74
+ protected function _assignDefaultValues() {
75
+ }
76
+ }
app/code/local/Mana/Db/Model/Observer.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Models/Observer */
10
+ /**
11
+ * This class observes certain (defined in etc/config.xml) events in the whole system and provides public methods - handlers for
12
+ * these events.
13
+ * @author Mana Team
14
+ *
15
+ */
16
+ class Mana_Db_Model_Observer {
17
+ protected function _getProcess() {
18
+ return Mage::getModel('index/process')->load('mana_db_replicator', 'indexer_code');
19
+ }
20
+ /* BASED ON SNIPPET: Models/Event handler */
21
+ /**
22
+ * Catches moment after database upgrade to rerun data replication actions (handles events
23
+ * "controller_action_predispatch", "core_config_data_save_commit_after")
24
+ * @param Varien_Event_Observer $observer
25
+ */
26
+ public function replicateAllIfPending($observer) {
27
+ if (Mage::registry('m_run_db_replication')) {
28
+ Mage::unregister('m_run_db_replication');
29
+ $this->_getProcess()->changeStatus(Mage_Index_Model_Process::STATUS_REQUIRE_REINDEX)->reindexAll();
30
+ }
31
+ }
32
+ /**
33
+ * Enter description here ...
34
+ * @param unknown_type $observer
35
+ * @deprecated
36
+ */
37
+ public function afterUpgrade($observer) {
38
+ }
39
+ /* BASED ON SNIPPET: Models/Event handler */
40
+ /**
41
+ * Runs data replication store-creation related actions (handles event "store_save_commit_after")
42
+ * @param Varien_Event_Observer $observer
43
+ */
44
+ public function afterStoreSave($observer) {
45
+ $dataObject = $observer->getEvent()->getDataObject();
46
+
47
+ if (!$dataObject->getdata('_m_prevent_replication')) {
48
+ Mage::helper('mana_db')->replicate(array(
49
+ 'trackKeys' => true,
50
+ 'filter' => array('core/store' => array('saved' => array($dataObject->getId()))),
51
+ ));
52
+ }
53
+ }
54
+ /* BASED ON SNIPPET: Models/Event handler */
55
+ /**
56
+ * Runs data replication store-deletion related actions (handles event "store_delete_commit_after")
57
+ * @param Varien_Event_Observer $observer
58
+ */
59
+ public function afterStoreDelete($observer) {
60
+ $dataObject = $observer->getEvent()->getDataObject();
61
+
62
+ if (!$dataObject->getdata('_m_prevent_replication')) {
63
+ Mage::helper('mana_db')->replicate(array(
64
+ 'trackKeys' => true,
65
+ 'filter' => array('core/store' => array('deleted' => array($dataObject->getId()))),
66
+ ));
67
+ }
68
+ }
69
+ /* BASED ON SNIPPET: Models/Event handler */
70
+ /**
71
+ * Runs data replication attribute-editing related actions (handles event "catalog_entity_attribute_save_commit_after")
72
+ * @param Varien_Event_Observer $observer
73
+ */
74
+ public function afterCatalogAttributeSave($observer) {
75
+ $dataObject = $observer->getEvent()->getDataObject();
76
+
77
+ if (!$dataObject->getdata('_m_prevent_replication')) {
78
+ Mage::helper('mana_db')->replicate(array(
79
+ 'trackKeys' => true,
80
+ 'filter' => array('eav/attribute' => array('saved' => array($dataObject->getId()))),
81
+ ));
82
+ }
83
+ }
84
+ /* BASED ON SNIPPET: Models/Event handler */
85
+ /**
86
+ * Runs data replication attribute-deletion related actions (handles event "catalog_entity_attribute_delete_commit_after")
87
+ * @param Varien_Event_Observer $observer
88
+ */
89
+ public function afterCatalogAttributeDelete($observer) {
90
+ $dataObject = $observer->getEvent()->getDataObject();
91
+
92
+ if (!$dataObject->getdata('_m_prevent_replication')) {
93
+ Mage::helper('mana_db')->replicate(array(
94
+ 'trackKeys' => true,
95
+ 'filter' => array('eav/attribute' => array('deleted' => array($dataObject->getId()))),
96
+ ));
97
+ }
98
+ }
99
+ /* BASED ON SNIPPET: Models/Event handler */
100
+ /**
101
+ * Runs data replication actions if related configuration changed (handles event "core_config_data_save_commit_after")
102
+ * @param Varien_Event_Observer $observer
103
+ */
104
+ public function afterConfigSave($observer) {
105
+ $configData = $observer->getEvent()->getObject();
106
+ if ($configData->getResourceName() == 'core/config_data' &&
107
+ Mage::helper('mana_db')->isReplicatedConfigChanged($configData))
108
+ {
109
+ Mage::register('m_run_db_replication', true);
110
+ }
111
+ }
112
+
113
+ }
app/code/local/Mana/Db/Model/Replication/Target.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ * PROPERTY METHODS
14
+ *
15
+ * @method string getEntityName()
16
+ * @method bool hasEntityName(()
17
+ * @method Mana_Db_Model_Replication_Target unsEntityName(()
18
+ * @method Mana_Db_Model_Replication_Target setEntityName(()
19
+ *
20
+ * @method bool getIsKeyFilterApplied()
21
+ * @method bool hasIsKeyFilterApplied(()
22
+ * @method Mana_Db_Model_Replication_Target unsIsKeyFilterApplied(()
23
+ * @method Mana_Db_Model_Replication_Target setIsKeyFilterApplied(()
24
+ *
25
+ * @method bool getReplicable()
26
+ * @method bool hasReplicable()
27
+ * @method Mana_Db_Replication_Target unsReplicable()
28
+ * @method Mana_Db_Replication_Target setReplicable()
29
+ *
30
+ * COLLECTION METHODS
31
+ *
32
+ * @method array getSourceEntityNames()
33
+ * @method string getSourceEntityName()
34
+ * @method bool hasSourceEntityName()
35
+ * @method Mana_Db_Model_Replication_Target setSourceEntityName()
36
+ * @method Mana_Db_Model_Replication_Target setSourceEntityNames()
37
+ * @method Mana_Db_Model_Replication_Target unsSourceEntityName()
38
+ *
39
+ * @method array getSavedKeys()
40
+ * @method string getSavedKey()
41
+ * @method bool hasSavedKey()
42
+ * @method Mana_Db_Model_Replication_Target setSavedKey()
43
+ * @method Mana_Db_Model_Replication_Target setSavedKeys()
44
+ * @method Mana_Db_Model_Replication_Target unsSavedKey()
45
+ *
46
+ * @method array getDeletedKeys()
47
+ * @method string getDeletedKey()
48
+ * @method bool hasDeletedKey()
49
+ * @method Mana_Db_Model_Replication_Target setDeletedKey()
50
+ * @method Mana_Db_Model_Replication_Target setDeletedKeys()
51
+ * @method Mana_Db_Model_Replication_Target unsDeletedKey()
52
+ *
53
+ * @method array getSelects()
54
+ * @method Varien_Db_Select getSelect()
55
+ * @method bool hasSelect()
56
+ * @method Mana_Db_Model_Replication_Target setSelect()
57
+ * @method Mana_Db_Model_Replication_Target setSelects()
58
+ * @method Mana_Db_Model_Replication_Target unsSelect()
59
+ *
60
+ *
61
+ */
62
+ class Mana_Db_Model_Replication_Target extends Mana_Core_Model_Object {
63
+ protected $_collections = array(
64
+ 'source_entity_names' => array(),
65
+ 'saved_keys' => array(),
66
+ 'deleted_keys' => array(),
67
+ 'selects' => array(),
68
+ );
69
+ }
app/code/local/Mana/Db/Model/Validation.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ * ARRAY METHODS
14
+ *
15
+ * @method array getErrors()
16
+ * @method string getError()
17
+ * @method Mana_Db_Model_Validation unsErrors()
18
+ * @method Mana_Db_Model_Validation unsError()
19
+ * @method Mana_Db_Model_Validation addError()
20
+ * @method Mana_Db_Model_Validation setErrors()
21
+ *
22
+ */
23
+ class Mana_Db_Model_Validation extends Mana_Core_Model_Object {
24
+ protected $_arrays = array(
25
+ 'errors' => array(),
26
+ );
27
+ }
app/code/local/Mana/Db/Model/Virtual/Result.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ * ARRAY METHODS
14
+ *
15
+ * @method array getColumns()
16
+ * @method string getColumn()
17
+ * @method Mana_Db_Model_Virtual_Result unsColumns()
18
+ * @method Mana_Db_Model_Virtual_Result unsColumn()
19
+ * @method Mana_Db_Model_Virtual_Result addColumn()
20
+ * @method Mana_Db_Model_Virtual_Result setColumns()
21
+ *
22
+ */
23
+ class Mana_Db_Model_Virtual_Result extends Mana_Core_Model_Object {
24
+ protected $_arrays = array(
25
+ 'columns' => array(),
26
+ );
27
+ }
app/code/local/Mana/Db/Resource/Edit/Session.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* BASED ON SNIPPET: Resources/Single model DB operations */
4
+ /**
5
+ * This resource model handles DB operations with a single model of type Mana_Db_Model_Edit_Session. All
6
+ * database specific code for Mana_Db_Model_Edit_Session should go here.
7
+ * @author Mana Team
8
+ */
9
+ class Mana_Db_Resource_Edit_Session extends Mage_Core_Model_Mysql4_Abstract {//Mage_Core_Model_Resource_Abstract
10
+ /**
11
+ * Resource initialization
12
+ */
13
+ protected function _construct() {
14
+ }
15
+
16
+ /**
17
+ * Retrieve connection for read data
18
+ */
19
+ protected function _getReadAdapter() {
20
+ return Mage::getSingleton('core/resource')->getConnection('read');
21
+ }
22
+
23
+ /**
24
+ * Retrieve connection for write data
25
+ * @return Zend_Db_Adapter_Abstract
26
+ */
27
+ protected function _getWriteAdapter() {
28
+ return Mage::getSingleton('core/resource')->getConnection('write');
29
+ }
30
+
31
+ public function begin() {
32
+ $this->beginTransaction();
33
+ try {
34
+ $db = $this->_getWriteAdapter();
35
+ $table = Mage::getSingleton('core/resource')->getTableName('mana_db/edit_session');
36
+
37
+ $db->delete($table, "id <> 0 AND (CURRENT_TIMESTAMP - created_at > 24*60*60)");
38
+ $db->insert($table, array());
39
+ $result = $db->lastInsertId($table);
40
+ $this->commit();
41
+ return $result;
42
+ }
43
+ catch (Exception $e) {
44
+ $this->rollBack();
45
+ throw $e;
46
+ }
47
+ }
48
+ }
app/code/local/Mana/Db/Resource/Object.php ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ abstract class Mana_Db_Resource_Object extends Mage_Core_Model_Mysql4_Abstract {
15
+ protected $_entityName;
16
+ protected function _init($mainTable, $idFieldName) {
17
+ parent::_init($mainTable, $idFieldName);
18
+ $this->_entityName = $mainTable;
19
+ }
20
+ public function getEntityName() {
21
+ return $this->_entityName;
22
+ }
23
+
24
+ public function loadByGlobalId($object, $globalId, $storeId) {
25
+ $read = $this->_getReadAdapter();
26
+
27
+ /* @var $select Varien_Db_Select */ $select = $this->_getLoadSelect('global_id', $globalId, $object);
28
+ $select->where($this->getMainTable().'.store_id'.'=?', $storeId);
29
+ $data = $read->fetchRow($select);
30
+ if ($data) {
31
+ $object->setData($data);
32
+ }
33
+
34
+ $this->unserializeFields($object);
35
+ $this->_afterLoad($object);
36
+
37
+ return $this;
38
+ }
39
+
40
+ protected function _getLoadSelect($field, $value, $object) {
41
+ $select = $this->_getReadAdapter()->select()
42
+ ->from($this->getMainTable(), null)
43
+ ->where($this->getMainTable().'.'.$field.'=?', $value);
44
+
45
+ if (!count($this->_columns) || isset($this->_columns['*'])) {
46
+ $select->columns($this->getMainTable().'.*');
47
+ $this->addVirtualColumns($select);
48
+ }
49
+ else {
50
+ $virtualColumns = $this->addVirtualColumns($select, $this->_columns);
51
+ foreach (array_diff($this->_columns, $virtualColumns) as $column) {
52
+ $this->getSelect()->columns($this->getMainTable().'.'.$column);
53
+ }
54
+ }
55
+
56
+ return $select;
57
+ }
58
+
59
+ public function getReplicationSources() {
60
+ $result = new Varien_Object(array('sources' => $this->_getReplicationSources()));
61
+ $targetName = $this->getEntityName();
62
+ Mage::dispatchEvent('m_db_sources', compact('targetName', 'result'));
63
+ return $result->getSources();
64
+ }
65
+ protected function _getReplicationSources() {
66
+ return array();
67
+ }
68
+
69
+ /**
70
+ * Enter description here ...
71
+ * @param Mana_Db_Model_Replication_Target $target
72
+ */
73
+ public function prepareReplicationUpdateSelects($target, $options) {
74
+ $this->_prepareReplicationUpdateSelects($target, $options);
75
+ Mage::dispatchEvent('m_db_update_tables', compact('target', 'options'));
76
+ Mage::dispatchEvent('m_db_update_columns', compact('target', 'options'));
77
+ }
78
+ /**
79
+ * Enter description here ...
80
+ * @param Mana_Db_Model_Replication_Target $target
81
+ */
82
+ protected function _prepareReplicationUpdateSelects($target, $options) {
83
+ }
84
+
85
+ /**
86
+ * Enter description here ...
87
+ * @param array $values
88
+ * @param array $options
89
+ */
90
+ public function processReplicationUpdate($values, $options) {
91
+ $object = $options['object'] ? $options['object'] : Mage::getModel($this->getEntityName());
92
+ $this->_processReplicationUpdate($object, $values, $options);
93
+ Mage::dispatchEvent('m_db_update_process', compact('object', 'values', 'options'));
94
+ return $object;
95
+ }
96
+
97
+ /**
98
+ * Enter description here ...
99
+ * @param Mana_Db_Model_Object $object
100
+ * @param array $values
101
+ * @param array $options
102
+ */
103
+ protected function _processReplicationUpdate($object, $values, $options) {
104
+ }
105
+
106
+ /**
107
+ * Enter description here ...
108
+ * @param Mana_Db_Model_Replication_Target $target
109
+ */
110
+ public function prepareReplicationInsertSelects($target, $options) {
111
+ $this->_prepareReplicationInsertSelects($target, $options);
112
+ Mage::dispatchEvent('m_db_insert_tables', compact('target', 'options'));
113
+ Mage::dispatchEvent('m_db_insert_columns', compact('target', 'options'));
114
+ }
115
+ /**
116
+ * Enter description here ...
117
+ * @param Mana_Db_Model_Replication_Target $target
118
+ */
119
+ protected function _prepareReplicationInsertSelects($target, $options) {
120
+ }
121
+
122
+ /**
123
+ * Enter description here ...
124
+ * @param array $values
125
+ * @param array $options
126
+ */
127
+ public function processReplicationInsert($values, $options) {
128
+ $object = $options['object'] ? $options['object'] : Mage::getModel($this->getEntityName());
129
+ $this->_processReplicationInsert($object, $values, $options);
130
+ Mage::dispatchEvent('m_db_insert_process', compact('object', 'values', 'options'));
131
+ return $object;
132
+ }
133
+
134
+ /**
135
+ * Enter description here ...
136
+ * @param Mana_Db_Model_Object $object
137
+ * @param array $values
138
+ * @param array $options
139
+ */
140
+ protected function _processReplicationInsert($object, $values, $options) {
141
+ }
142
+
143
+ /**
144
+ * Enter description here ...
145
+ * @param Mana_Db_Model_Replication_Target $target
146
+ */
147
+ public function prepareReplicationDeleteSelects($target, $options) {
148
+ $this->_prepareReplicationDeleteSelects($target, $options);
149
+ Mage::dispatchEvent('m_db_delete_tables', compact('target', 'options'));
150
+ Mage::dispatchEvent('m_db_delete_columns', compact('target', 'options'));
151
+ }
152
+ /**
153
+ * Enter description here ...
154
+ * @param Mana_Db_Model_Replication_Target $target
155
+ */
156
+ protected function _prepareReplicationDeleteSelects($target, $options) {
157
+ }
158
+
159
+ /**
160
+ * Enter description here ...
161
+ * @param array $values
162
+ * @param array $options
163
+ */
164
+ public function processReplicationDelete($values, $options) {
165
+ $this->_processReplicationDelete($values, $options);
166
+ $entity_name = $this->getEntityName();
167
+ Mage::dispatchEvent('m_db_delete_process', compact('entity_name', 'values', 'options'));
168
+ }
169
+
170
+ /**
171
+ * Enter description here ...
172
+ * @param array $values
173
+ * @param array $options
174
+ */
175
+ protected function _processReplicationDelete($values, $options) {
176
+ }
177
+
178
+ protected $_columns = array();
179
+ public function addColumnToSelect($column) {
180
+ $this->_columns[$column] = $column;
181
+ return $this;
182
+ }
183
+ public function addVirtualColumns($select, $columns = null) {
184
+ $result = Mage::getModel('mana_db/virtual_result');
185
+ $this->_addVirtualColumns($result, $select, $columns);
186
+ $entity_name = $this->getEntityName();
187
+ Mage::dispatchEvent('m_db_virtual_columns', compact('entity_name', 'result', 'select', 'columns'));
188
+ return $result->getColumns();
189
+ }
190
+ /**
191
+ * Enter description here ...
192
+ * @param Mana_Db_Model_Virtual_Result $result
193
+ * @param Varien_Db_Select $select
194
+ * @param array $columns
195
+ */
196
+ protected function _addVirtualColumns($result, $select, $columns = null) {
197
+ }
198
+
199
+ public function addEditedData($object, $fields, $use_default) {
200
+ $this->_addEditedData($object, $fields, $use_default);
201
+ Mage::dispatchEvent('m_db_add_edited_data', compact('object', 'fields', 'use_default'));
202
+ }
203
+ protected function _addEditedData($object, $fields, $useDefault) {
204
+ }
205
+ public function addEditedDetails($object, $request) {
206
+ $this->_addEditedDetails($object, $request);
207
+ Mage::dispatchEvent('m_db_add_edited_details', compact('object', 'request'));
208
+ }
209
+ protected function _addEditedDetails($object, $request) {
210
+ }
211
+ }
app/code/local/Mana/Db/Resource/Object/Collection.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Db
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Enter description here ...
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Db_Resource_Object_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
15
+ public function getEntityName() {
16
+ return $this->getResourceModelName();
17
+ }
18
+ protected function _beforeLoad() {
19
+ if (!count($this->_columns) || isset($this->_columns['*'])) {
20
+ $this->getSelect()->columns('main_table.*');
21
+ $this->addVirtualColumns($this->getSelect());
22
+ }
23
+ else {
24
+ $virtualColumns = $this->addVirtualColumns($this->getSelect(), $this->_columns);
25
+ foreach (array_diff($this->_columns, $virtualColumns) as $column) {
26
+ $this->getSelect()->columns('main_table.'.$column);
27
+ }
28
+ }
29
+ $this->_renderEditFilter();
30
+ parent::_beforeLoad();
31
+ return $this;
32
+ }
33
+ public function addStoreFilter($store) {
34
+ $this->addFieldToFilter('store_id', $store->getId());
35
+ return $this;
36
+ }
37
+
38
+ protected $_columns = array();
39
+ public function addColumnToSelect($column) {
40
+ if (is_array($column)) {
41
+ foreach ($column as $item) {
42
+ $this->_columns[$item] = $item;
43
+ }
44
+ }
45
+ else {
46
+ $this->_columns[$column] = $column;
47
+ }
48
+ return $this;
49
+ }
50
+ public function addVirtualColumns($select, $columns = null) {
51
+ $result = Mage::getModel('mana_db/virtual_result');
52
+ $this->_addVirtualColumns($result, $select, $columns);
53
+ $entity_name = $this->getEntityName();
54
+ Mage::dispatchEvent('m_db_virtual_collection_columns', compact('entity_name', 'result', 'select', 'columns'));
55
+ return $result->getColumns();
56
+ }
57
+ /**
58
+ * Enter description here ...
59
+ * @param Mana_Db_Model_Virtual_Result $result
60
+ * @param Varien_Db_Select $select
61
+ * @param array $columns
62
+ */
63
+ protected function _addVirtualColumns($result, $select, $columns = null) {
64
+ }
65
+ protected $_editFilter = null;
66
+ protected $_parentCondition = '';
67
+ public function setEditFilter($editFilter, $parentCondition = '') {
68
+ $this->_editFilter = $editFilter;
69
+ $this->_parentCondition = $parentCondition;
70
+ return $this;
71
+ }
72
+ protected function _renderEditFilter() {
73
+ $alias = 'main_table';
74
+ if (is_array($this->_editFilter)) {
75
+ $sql = count($this->_editFilter['saved'])
76
+ ? $this->getConnection()->quoteInto("$alias.edit_status = 0 AND $alias.id NOT IN (?)", array_keys($this->_editFilter['saved']))
77
+ : "$alias.edit_status = 0";
78
+ if ($this->_parentCondition) {
79
+ $sql .= " AND ($alias.{$this->_parentCondition})";
80
+ }
81
+ if (count($this->_editFilter['saved'])) {
82
+ $sql = "($sql) OR ({$this->getConnection()->quoteInto(
83
+ "$alias.edit_status <> 0 AND $alias.edit_session_id = ?", $this->_editFilter['sessionId'])})";
84
+ }
85
+ if (count($this->_editFilter['deleted'])) {
86
+ $sql = "($sql)".
87
+ " AND ({$this->getConnection()->quoteInto("$alias.id NOT IN (?)", $this->_editFilter['deleted'])})";
88
+ " AND ({$this->getConnection()->quoteInto("$alias.edit_status NOT IN (?)", $this->_editFilter['deleted'])})";
89
+ }
90
+ $this->getSelect()->where($sql);
91
+ }
92
+ elseif ($this->_editFilter) {
93
+ $this->getSelect()->where("$alias.edit_status = 0");
94
+ }
95
+ }
96
+ }
app/code/local/Mana/Db/Resource/Replicate.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* BASED ON SNIPPET: Resources/Single model DB operations */
4
+ /**
5
+ * This resource model handles DB operations with a single model of type Mana_Db_Model_Edit_Session. All
6
+ * database specific code for Mana_Db_Model_Edit_Session should go here.
7
+ * @author Mana Team
8
+ */
9
+ class Mana_Db_Resource_Replicate extends Mage_Core_Model_Mysql4_Abstract {//Mage_Core_Model_Resource_Abstract
10
+ /**
11
+ * Resource initialization
12
+ */
13
+ protected function _construct() {
14
+ }
15
+
16
+ /**
17
+ * Retrieve connection for read data
18
+ */
19
+ protected function _getReadAdapter() {
20
+ return Mage::getSingleton('core/resource')->getConnection('read');
21
+ }
22
+
23
+ /**
24
+ * Retrieve connection for write data
25
+ * @return Zend_Db_Adapter_Abstract
26
+ */
27
+ protected function _getWriteAdapter() {
28
+ return Mage::getSingleton('core/resource')->getConnection('write');
29
+ }
30
+ }
app/code/local/Mana/Db/etc/config.xml ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ @category Mana
4
+ @package Mana_Db
5
+ @copyright Copyright (c) http://www.manadev.com
6
+ @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ -->
8
+ <!-- BASED ON SNIPPET: New Module/etc/config.xml -->
9
+ <config>
10
+ <!-- This section registers module with Magento system. -->
11
+ <modules>
12
+ <Mana_Db>
13
+ <!-- This version number identifies version of database tables specific to this extension. It is written to
14
+ core_resource table. -->
15
+ <version>12.04.16.22</version>
16
+ </Mana_Db>
17
+ </modules>
18
+ <!-- This section contains module settings which are merged into global configuration during each page load,
19
+ each ajax request. -->
20
+ <global>
21
+ <!-- This section registers helper classes to be accessible through Mage::helper() method. Mana_Db_Helper_Data
22
+ class is accessible through Mage::helper('mana_db') call, other Mana_Db_Helper_XXX_YYY classes are accessible
23
+ through Mage::helper('mana_db/xxx_yyy') call. -->
24
+ <helpers>
25
+ <mana_db>
26
+ <!-- This says that string 'mana_db' corresponds to Mana_Db_Helper pseudo-namespace in
27
+ Mage::helper() calls. -->
28
+ <class>Mana_Db_Helper</class>
29
+ </mana_db>
30
+ </helpers>
31
+ <!-- BASED ON SNIPPET: Models/Model support (config.xml) -->
32
+ <!-- This section registers model classes to be accessible through Mage::getModel('<model type>') and through
33
+ Mage::getSingleton('<model type>') calls. That is, Mana_Db_Model_XXX_YYY classes are accessible as
34
+ 'mana_db/xxx_yyy' type strings both in getModel() and getSingleton() calls. -->
35
+ <models>
36
+ <!-- This says that string 'mana_db' corresponds to Mana_Db_Model pseudo-namespace in
37
+ getModel() and getSingleton() calls. -->
38
+ <mana_db>
39
+ <class>Mana_Db_Model</class>
40
+ <!-- BASED ON SNIPPET: Resources/Declare resource section (config.xml) -->
41
+ <!-- This tells Magento to read config/global/models/mana_db_resources sections and register
42
+ resource model information from there -->
43
+ <resourceModel>mana_db_resources</resourceModel>
44
+ </mana_db>
45
+ <!-- BASED ON SNIPPET: Resources/Resource support (config.xml) -->
46
+ <!-- This says that string 'mana_db' corresponds to Mana_Db_Resource pseudo-namespace in
47
+ getResourceModel() calls. -->
48
+ <mana_db_resources>
49
+ <class>Mana_Db_Resource</class>
50
+ <entities>
51
+ <db><table>m_db</table></db>
52
+ <log><table>m_db_log</table></log>
53
+ <edit_session><table>m_edit_session</table></edit_session>
54
+ <!-- INSERT HERE: table-entity mappings -->
55
+ </entities>
56
+ </mana_db_resources>
57
+ <!-- INSERT HERE: rewrites, ... -->
58
+ </models>
59
+ <!-- BASED ON SNIPPET: Resources/Installation script support (config.xml) -->
60
+ <!-- This tells Magento to analyze sql/mana_db_setup directory for install/upgrade scripts.
61
+ Installation scripts should be named as 'mysql4-install-<new version>.php'.
62
+ Upgrade scripts should be named as mysql4-upgrade-<current version>-<new version>.php -->
63
+ <resources>
64
+ <mana_db_setup>
65
+ <setup>
66
+ <module>Mana_Db</module>
67
+ </setup>
68
+ </mana_db_setup>
69
+ </resources>
70
+ <index>
71
+ <indexer>
72
+ <mana_db_replicator>
73
+ <model>mana_db/indexer</model>
74
+ </mana_db_replicator>
75
+ </indexer>
76
+ </index>
77
+ <!-- BASED ON SNIPPET: New Models/Event support (config.xml) -->
78
+ <!-- This section registers event handlers of this module defined in Mana_Db_Model_Observer with events defined in other
79
+ module throughout the system. So when some code in other module invokes an event mentioned in this section, handler
80
+ method of Mana_Db_Model_Observer class gets called. -->
81
+ <events>
82
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
83
+ <controller_action_predispatch><!-- this is event name this module listens for -->
84
+ <observers>
85
+ <mana_db>
86
+ <class>mana_db/observer</class> <!-- model name of class containing event handler methods -->
87
+ <method>replicateAllIfPending</method> <!-- event handler method name -->
88
+ </mana_db>
89
+ </observers>
90
+ </controller_action_predispatch>
91
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
92
+ <store_save_commit_after><!-- this is event name this module listens for -->
93
+ <observers>
94
+ <mana_db>
95
+ <class>mana_db/observer</class> <!-- model name of class containing event handler methods -->
96
+ <method>replicateAllIfPending</method> <!-- event handler method name -->
97
+ </mana_db>
98
+ </observers>
99
+ </store_save_commit_after>
100
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
101
+ <store_delete_commit_after><!-- this is event name this module listens for -->
102
+ <observers>
103
+ <mana_db>
104
+ <class>mana_db/observer</class> <!-- model name of class containing event handler methods -->
105
+ <method>afterStoreDelete</method> <!-- event handler method name -->
106
+ </mana_db>
107
+ </observers>
108
+ </store_delete_commit_after>
109
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
110
+ <catalog_entity_attribute_save_commit_after><!-- this is event name this module listens for -->
111
+ <observers>
112
+ <mana_db>
113
+ <class>mana_db/observer</class> <!-- model name of class containing event handler methods -->
114
+ <method>afterCatalogAttributeSave</method> <!-- event handler method name -->
115
+ </mana_db>
116
+ </observers>
117
+ </catalog_entity_attribute_save_commit_after>
118
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
119
+ <catalog_entity_attribute_delete_commit_after><!-- this is event name this module listens for -->
120
+ <observers>
121
+ <mana_db>
122
+ <class>mana_db/observer</class> <!-- model name of class containing event handler methods -->
123
+ <method>afterCatalogAttributeDelete</method> <!-- event handler method name -->
124
+ </mana_db>
125
+ </observers>
126
+ </catalog_entity_attribute_delete_commit_after>
127
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
128
+ <core_config_data_save_commit_after><!-- this is event name this module listens for -->
129
+ <observers>
130
+ <mana_db>
131
+ <class>mana_db/observer</class> <!-- model name of class containing event handler methods -->
132
+ <method>replicateAllIfPending</method> <!-- event handler method name -->
133
+ </mana_db>
134
+ </observers>
135
+ </core_config_data_save_commit_after>
136
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
137
+ <model_save_commit_after><!-- this is event name this module listens for -->
138
+ <observers>
139
+ <mana_db>
140
+ <class>mana_db/observer</class> <!-- model name of class containing event handler methods -->
141
+ <method>afterConfigSave</method> <!-- event handler method name -->
142
+ </mana_db>
143
+ </observers>
144
+ </model_save_commit_after>
145
+ </events>
146
+ <!-- INSERT HERE: blocks, models, ... -->
147
+ </global>
148
+ <!-- BASED ON SNIPPET: Static Visuals/Adminhtml section (config.xml) -->
149
+ <!-- This section enables static visual changes in admin area. -->
150
+ <adminhtml>
151
+ <!-- BASED ON SNIPPET: Translation support/Adminhtml (config.xml) -->
152
+ <!-- This section registers additional translation file with our module-specific strings to be loaded
153
+ during admin area request processing -->
154
+ <translate>
155
+ <modules>
156
+ <Mana_Db>
157
+ <files>
158
+ <default>Mana_Db.csv</default>
159
+ </files>
160
+ </Mana_Db>
161
+ </modules>
162
+ </translate>
163
+ <!-- INSERT HERE: layout, translate, routers -->
164
+ </adminhtml>
165
+ <!-- BASED ON SNIPPET: Static Visuals/Frontend section (config.xml) -->
166
+ <!-- This section enables static visual changes in store frontend. -->
167
+ <frontend>
168
+ <!-- BASED ON SNIPPET: Translation support/Frontend (config.xml) -->
169
+ <!-- This section registers additional translation file with our module-specific strings to be loaded
170
+ during frontend request processing -->
171
+ <translate>
172
+ <modules>
173
+ <Mana_Db>
174
+ <files>
175
+ <default>Mana_Db.csv</default>
176
+ </files>
177
+ </Mana_Db>
178
+ </modules>
179
+ </translate>
180
+ <!-- INSERT HERE: layout, translate, routers -->
181
+ </frontend>
182
+ <!-- INSERT HERE: adminhtml, frontend, ... -->
183
+ </config>
app/code/local/Mana/Db/sql/mana_db_setup/mysql4-install-11.09.28.09.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
3
+ if (defined('COMPILER_INCLUDE_PATH')) {
4
+ throw new Exception(Mage::helper('mana_core')->__('This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.'));
5
+ }
6
+
7
+ /* @var $installer Mana_Core_Resource_Eav_Setup */
8
+ $installer = $this;
9
+
10
+ $installer->startSetup();
11
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
12
+ $table = 'm_db_log';
13
+ $installer->run("
14
+ DROP TABLE IF EXISTS `{$this->getTable($table)}`;
15
+ CREATE TABLE `{$this->getTable($table)}` (
16
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
17
+ `script_filename` varchar(128) NOT NULL default '',
18
+ `undo` TEXT NOT NULL,
19
+
20
+ PRIMARY KEY (`id`),
21
+ KEY `script_filename` (`script_filename`)
22
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
23
+
24
+ ");
25
+
26
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
27
+ $table = 'm_db';
28
+ $installer->run("
29
+ DROP TABLE IF EXISTS `{$this->getTable($table)}`;
30
+ CREATE TABLE `{$this->getTable($table)}` (
31
+ `config` TEXT NOT NULL
32
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
33
+ ");
34
+
35
+ $installer->endSetup();
app/code/local/Mana/Db/sql/mana_db_setup/mysql4-upgrade-11.09.28.09-11.09.28.10.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
4
+ if (defined('COMPILER_INCLUDE_PATH')) {
5
+ throw new Exception(Mage::helper('mana_core')->__('This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.'));
6
+ }
7
+
8
+ /* @var $installer Mage_Core_Model_Resource_Setup */
9
+ $installer = $this;
10
+
11
+ $installer->startSetup();
12
+
13
+ $installer->endSetup();
14
+
app/code/local/Mana/Db/sql/mana_db_setup/mysql4-upgrade-11.10.08.23-11.10.20.22.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
4
+ if (defined('COMPILER_INCLUDE_PATH')) {
5
+ throw new Exception(Mage::helper('mana_core')->__('This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.'));
6
+ }
7
+
8
+ /* @var $installer Mage_Core_Model_Resource_Setup */
9
+ $installer = $this;
10
+
11
+ $installer->startSetup();
12
+
13
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
14
+ $table = 'mana_db/edit_session';
15
+ if (!$installer->tableExists($installer->getTable($table))) {
16
+ $installer->run("
17
+ CREATE TABLE `{$installer->getTable($table)}` (
18
+ `id` BIGINT NOT NULL auto_increment,
19
+ `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
20
+ PRIMARY KEY (`id`),
21
+ KEY `created_at` (`created_at`)
22
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
23
+ INSERT INTO `{$installer->getTable($table)}` (`id`) VALUES (0);
24
+ ");
25
+ }
26
+ $installer->endSetup();
27
+
app/code/local/Mana/Filters/Block/Filter.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * Block type for showing options for filter based on custom attribute
10
+ * @author Mana Team
11
+ * Injected into layout instead of standard catalog/layer_filter_attribute in Mana_Filters_Block_View_Category::_initBlocks.
12
+ */
13
+ class Mana_Filters_Block_Filter extends Mage_Catalog_Block_Layer_Filter_Abstract {
14
+ /**
15
+ * This function is typically called to initialize underlying model of filter and apply it to current
16
+ * product set if needed. Here we leave it as is except that we assign template file here not in constructor,
17
+ * not how standard Magento does.
18
+ * @see Mage_Catalog_Block_Layer_Filter_Abstract::init()
19
+ */
20
+ public function init() {
21
+ $this->setTemplate((string)$this->getDisplayOptions()->template);
22
+ $this->_filterModelName = (string)$this->getDisplayOptions()->model;
23
+ return parent::init();
24
+ }
25
+
26
+ protected function _prepareFilter() {
27
+ if ($this->getAttributeModel()) {
28
+ $this->_filter->setAttributeModel($this->getAttributeModel());
29
+ }
30
+ $this->_filter
31
+ ->setFilterOptions($this->getFilterOptions())
32
+ ->setDisplayOptions($this->getDisplayOptions())
33
+ ->setMode($this->getMode());
34
+ return $this;
35
+ }
36
+
37
+ /**
38
+ * Returns underlying model object which contains actual filter data
39
+ * @return Mage_Catalog_Model_Layer_Filter_Attribute
40
+ */
41
+ public function getFilter() {
42
+ return $this->_filter;
43
+ }
44
+
45
+ public function getName() {
46
+ return $this->getFilterOptions()->getName();
47
+ }
48
+
49
+ public function getSelectedSeoValues() {
50
+ $result = array();
51
+ foreach ($this->getItems() as $item) {
52
+ /* @var $item Mana_Filters_Model_Item */
53
+ if ($item->getMSelected()) {
54
+ $result[] = $item->getSeoValue();
55
+ }
56
+ }
57
+ return implode('_', $result);
58
+ }
59
+ }
app/code/local/Mana/Filters/Block/Filter/Attribute.php DELETED
@@ -1,42 +0,0 @@
1
- <?php
2
- /**
3
- * @category Mana
4
- * @package Mana_Filters
5
- * @copyright Copyright (c) http://www.manadev.com
6
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
- */
8
- /**
9
- * Block type for showing options for filter based on custom attribute
10
- * @author Mana Team
11
- * Injected into layout instead of standard catalog/layer_filter_attribute in Mana_Filters_Block_View_Category::_initBlocks.
12
- */
13
- class Mana_Filters_Block_Filter_Attribute extends Mage_Catalog_Block_Layer_Filter_Attribute {
14
- /**
15
- * Overridden constructor injects our template instead of standard one
16
- */
17
- public function __construct()
18
- {
19
- parent::__construct();
20
- $this->_filterModelName = 'mana_filters/filter_attribute';
21
- }
22
-
23
- /**
24
- * This function is typically called to initialize underlying model of filter and apply it to current
25
- * product set if needed. Here we leave it as is except that we assign template file here not in constructor,
26
- * not how standard Magento does.
27
- * @see Mage_Catalog_Block_Layer_Filter_Abstract::init()
28
- */
29
- public function init() {
30
- /* @var $ext Mana_Filters_Helper_Extended */ $ext = Mage::helper(strtolower('Mana_Filters/Extended'));
31
- $this->setTemplate($ext->getFilterTemplate($this));
32
- return parent::init();
33
- }
34
-
35
- /**
36
- * Returns underlying model object which contains actual filter data
37
- * @return Mage_Catalog_Model_Layer_Filter_Attribute
38
- */
39
- public function getFilter() {
40
- return $this->_filter;
41
- }
42
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Mana/Filters/Block/Filter/Attribute/Search.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
- /**
3
- * @category Mana
4
- * @package Mana_Filters
5
- * @copyright Copyright (c) http://www.manadev.com
6
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
- */
8
- /**
9
- * Block type for showing options for filter based on custom attribute, specific for search
10
- * @author Mana Team
11
- * Injected into layout instead of standard catalog/layer_filter_attribute in Mana_Filters_Block_View_Search::_initBlocks.
12
- */
13
- class Mana_Filters_Block_Filter_Attribute_Search extends Mana_Filters_Block_Filter_Attribute {
14
- public function __construct()
15
- {
16
- parent::__construct();
17
- $this->_filterModelName = 'mana_filters/filter_attribute_search';
18
- }
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Mana/Filters/Block/Filter/Category.php DELETED
@@ -1,41 +0,0 @@
1
- <?php
2
- /**
3
- * @category Mana
4
- * @package Mana_Filters
5
- * @copyright Copyright (c) http://www.manadev.com
6
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
- */
8
- /**
9
- * Block type for showing subcategory filter
10
- * @author Mana Team
11
- * Injected into layout instead of standard catalog/layer_filter_category in Mana_Filters_Block_View_Category::_initBlocks.
12
- */
13
- class Mana_Filters_Block_Filter_Category extends Mage_Catalog_Block_Layer_Filter_Category {
14
- /**
15
- * Overridden constructor injects our template instead of standard one
16
- */
17
- public function __construct()
18
- {
19
- parent::__construct();
20
- $this->_filterModelName = 'mana_filters/filter_category';
21
- }
22
- /**
23
- * This function is typically called to initialize underlying model of filter and apply it to current
24
- * product set if needed. Here we leave it as is except that we assign template file here not in constructor,
25
- * not how standard Magento does.
26
- * @see Mage_Catalog_Block_Layer_Filter_Abstract::init()
27
- */
28
- public function init() {
29
- /* @var $ext Mana_Filters_Helper_Extended */ $ext = Mage::helper(strtolower('Mana_Filters/Extended'));
30
- $this->setTemplate($ext->getFilterTemplate($this));
31
- return parent::init();
32
- }
33
-
34
- /**
35
- * Returns underlying model object which contains actual filter data
36
- * @return Mage_Catalog_Model_Layer_Filter_Attribute
37
- */
38
- public function getFilter() {
39
- return $this->_filter;
40
- }
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Mana/Filters/Block/Filter/Decimal.php DELETED
@@ -1,23 +0,0 @@
1
- <?php
2
- /**
3
- * @category Mana
4
- * @package Mana_Filters
5
- * @copyright Copyright (c) http://www.manadev.com
6
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
- */
8
- /**
9
- * Block type for showing options for filter based on custom number attribute
10
- * @author Mana Team
11
- * Injected into layout instead of standard catalog/layer_filter_decimal in Mana_Filters_Block_View_Category::_initBlocks.
12
- */
13
- class Mana_Filters_Block_Filter_Decimal extends Mage_Catalog_Block_Layer_Filter_Decimal {
14
- // NO CHANGES HERE, BUT PROBABLY THEY WILL COME IN NEAR FUTURE
15
-
16
- /**
17
- * Returns underlying model object which contains actual filter data
18
- * @return Mage_Catalog_Model_Layer_Filter_Attribute
19
- */
20
- public function getFilter() {
21
- return $this->_filter;
22
- }
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Mana/Filters/Block/Filter/Price.php DELETED
@@ -1,41 +0,0 @@
1
- <?php
2
- /**
3
- * @category Mana
4
- * @package Mana_Filters
5
- * @copyright Copyright (c) http://www.manadev.com
6
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
- */
8
- /**
9
- * Block type for showing price filtering options
10
- * @author Mana Team
11
- * Injected into layout instead of standard catalog/layer_filter_price in Mana_Filters_Block_View_Category::_initBlocks.
12
- */
13
- class Mana_Filters_Block_Filter_Price extends Mage_Catalog_Block_Layer_Filter_Price {
14
- /**
15
- * Overridden constructor injects our template instead of standard one
16
- */
17
- public function __construct()
18
- {
19
- parent::__construct();
20
- $this->_filterModelName = 'mana_filters/filter_price';
21
- }
22
- /**
23
- * This function is typically called to initialize underlying model of filter and apply it to current
24
- * product set if needed. Here we leave it as is except that we assign template file here not in constructor,
25
- * not how standard Magento does.
26
- * @see Mage_Catalog_Block_Layer_Filter_Abstract::init()
27
- */
28
- public function init() {
29
- /* @var $ext Mana_Filters_Helper_Extended */ $ext = Mage::helper(strtolower('Mana_Filters/Extended'));
30
- $this->setTemplate($ext->getFilterTemplate($this));
31
- return parent::init();
32
- }
33
-
34
- /**
35
- * Returns underlying model object which contains actual filter data
36
- * @return Mage_Catalog_Model_Layer_Filter_Attribute
37
- */
38
- public function getFilter() {
39
- return $this->_filter;
40
- }
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Mana/Filters/Block/Layer.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Filters_Block_Layer extends Mage_Core_Block_Template {
13
+ public function setCategoryId($id)
14
+ {
15
+ $category = Mage::getModel('catalog/category')->load($id);
16
+ if ($category->getId()) {
17
+ Mage::getSingleton('catalog/layer')->setCurrentCategory($category);
18
+ }
19
+ }
20
+ }
app/code/local/Mana/Filters/Block/Search.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * Block type for showing filters in search pages.
10
+ * @author Mana Team
11
+ * Injected into layout instead of standard catalogsearch/layer in layout XML file.
12
+ */
13
+ class Mana_Filters_Block_Search extends Mage_CatalogSearch_Block_Layer {
14
+ protected $_mode = 'search';
15
+
16
+ /**
17
+ * This method is called during page rendering to generate additional child blocks for this block.
18
+ * @return Mana_Filters_Block_View_Category
19
+ * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
20
+ * changes are marked with comments.
21
+ * @see app/code/core/Mage/Catalog/Block/Layer/Mage_Catalog_Block_Layer_View::_prepareLayout()
22
+ */
23
+ protected function _prepareLayout()
24
+ {
25
+ $stateBlock = $this->getLayout()->createBlock('mana_filters/state')
26
+ ->setLayer($this->getLayer());
27
+ $this->setChild('layer_state', $stateBlock);
28
+
29
+ foreach (Mage::helper('mana_filters')->getFilterOptionsCollection() as $filterOptions) {
30
+ $displayOptions = $filterOptions->getDisplayOptions();
31
+ $block = $this->getLayout()->createBlock((string)$displayOptions->block, '', array(
32
+ 'filter_options' => $filterOptions,
33
+ 'display_options' => $displayOptions,
34
+ ))->setLayer($this->getLayer());
35
+ if ($attribute = $filterOptions->getAttribute()) {
36
+ $block->setAttributeModel($attribute);
37
+ }
38
+ $block->setMode($this->_mode)->init();
39
+ $this->setChild($filterOptions->getCode() . '_filter', $block);
40
+ }
41
+
42
+ $this->getLayer()->apply();
43
+
44
+ return $this;
45
+ }
46
+
47
+ public function getFilters() {
48
+ $filters = array();
49
+ foreach (Mage::helper('mana_filters')->getFilterOptionsCollection() as $filterOptions) {
50
+ if ($filterOptions->getIsEnabledInSearch()) {
51
+ $filters[] = $this->getChild($filterOptions->getCode() . '_filter');
52
+ }
53
+ }
54
+ return $filters;
55
+ }
56
+ public function getClearUrl() {
57
+ return Mage::helper('mana_filters')->getClearUrl();
58
+ }
59
+ }
app/code/local/Mana/Filters/Block/State.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Refined block which shows currently applied filter values
11
+ * @author Mana Team
12
+ */
13
+ class Mana_Filters_Block_State extends Mage_Catalog_Block_Layer_State {
14
+ public function getClearUrl() {
15
+ return Mage::helper('mana_filters')->getClearUrl();
16
+ }
17
+ public function __construct()
18
+ {
19
+ parent::__construct();
20
+ $this->setTemplate('mana/filters/state.phtml');
21
+ }
22
+ public function getValueHtml($item) {
23
+ $result = new Varien_Object();
24
+ $block = $this;
25
+ Mage::dispatchEvent('m_filter_value_html', compact('block', 'item', 'result'));
26
+ return $result->getHtml() ? $result->getHtml() : '';
27
+ }
28
+ }
app/code/local/Mana/Filters/Block/View.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * Block type for showing filters in category view pages.
10
+ * @author Mana Team
11
+ * Injected into layout instead of standard catalog/layer_view in layout XML file.
12
+ */
13
+ class Mana_Filters_Block_View extends Mage_Catalog_Block_Layer_View {
14
+ protected $_mode = 'category';
15
+
16
+ /**
17
+ * This method is called during page rendering to generate additional child blocks for this block.
18
+ * @return Mana_Filters_Block_View_Category
19
+ * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
20
+ * changes are marked with comments.
21
+ * @see app/code/core/Mage/Catalog/Block/Layer/Mage_Catalog_Block_Layer_View::_prepareLayout()
22
+ */
23
+ protected function _prepareLayout()
24
+ {
25
+ $stateBlock = $this->getLayout()->createBlock('mana_filters/state')
26
+ ->setLayer($this->getLayer());
27
+ $this->setChild('layer_state', $stateBlock);
28
+
29
+ foreach (Mage::helper('mana_filters')->getFilterOptionsCollection() as $filterOptions) {
30
+ $displayOptions = $filterOptions->getDisplayOptions();
31
+ $block = $this->getLayout()->createBlock((string)$displayOptions->block, '', array(
32
+ 'filter_options' => $filterOptions,
33
+ 'display_options' => $displayOptions,
34
+ ))->setLayer($this->getLayer());
35
+ if ($attribute = $filterOptions->getAttribute()) {
36
+ $block->setAttributeModel($attribute);
37
+ }
38
+ $block->setMode($this->_mode)->init();
39
+ $this->setChild($filterOptions->getCode() . '_filter', $block);
40
+ }
41
+
42
+ $this->getLayer()->apply();
43
+
44
+ return $this;
45
+ }
46
+
47
+ public function getFilters() {
48
+ $filters = array();
49
+ foreach (Mage::helper('mana_filters')->getFilterOptionsCollection() as $filterOptions) {
50
+ if ($filterOptions->getIsEnabled()) {
51
+ $filters[] = $this->getChild($filterOptions->getCode() . '_filter');
52
+ }
53
+ }
54
+ return $filters;
55
+ }
56
+ public function getClearUrl() {
57
+ return Mage::helper('mana_filters')->getClearUrl();
58
+ }
59
+ }
app/code/local/Mana/Filters/Block/View/Category.php DELETED
@@ -1,75 +0,0 @@
1
- <?php
2
- /**
3
- * @category Mana
4
- * @package Mana_Filters
5
- * @copyright Copyright (c) http://www.manadev.com
6
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
- */
8
- /**
9
- * Block type for showing filters in category view pages.
10
- * @author Mana Team
11
- * Injected into layout instead of standard catalog/layer_view in layout XML file.
12
- */
13
- class Mana_Filters_Block_View_Category extends Mage_Catalog_Block_Layer_View {
14
-
15
- /**
16
- * This method is called during page rendering to generate additional child blocks for this block.
17
- * @return Mana_Filters_Block_View_Category
18
- * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
19
- * changes are marked with comments.
20
- * @see app/code/core/Mage/Catalog/Block/Layer/Mage_Catalog_Block_Layer_View::_prepareLayout()
21
- */
22
- protected function _prepareLayout()
23
- {
24
- $stateBlock = $this->getLayout()->createBlock($this->_stateBlockName)
25
- ->setLayer($this->getLayer());
26
-
27
- $categoryBlock = $this->getLayout()->createBlock($this->_categoryBlockName)
28
- ->setLayer($this->getLayer())
29
- ->init();
30
-
31
- $this->setChild('layer_state', $stateBlock);
32
- $this->setChild('category_filter', $categoryBlock);
33
-
34
- $filterableAttributes = $this->_getFilterableAttributes();
35
- foreach ($filterableAttributes as $attribute) {
36
- if ($attribute->getAttributeCode() == 'price') {
37
- $filterBlockName = $this->_priceFilterBlockName;
38
- }
39
- elseif ($attribute->getBackendType() == 'decimal') {
40
- $filterBlockName = $this->_decimalFilterBlockName;
41
- }
42
- else {
43
- $filterBlockName = $this->_attributeFilterBlockName;
44
- }
45
-
46
- $this->setChild($attribute->getAttributeCode() . '_filter',
47
- $this->getLayout()->createBlock($filterBlockName)
48
- ->setLayer($this->getLayer())
49
- ->setAttributeModel($attribute)
50
- ->init());
51
- }
52
-
53
- $this->getLayer()->apply();
54
-
55
- return $this;
56
- }
57
-
58
- /**
59
- * This method is called during page rendering to determine block types to use inside layered navigation block.
60
- * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
61
- * changes are marked with comments.
62
- * @see Mage_Catalog_Block_Layer_View::_initBlocks()
63
- */
64
- protected function _initBlocks()
65
- {
66
- $this->_stateBlockName = 'catalog/layer_state';
67
- // MANA BEGIN: replace standard block types with ours
68
- $this->_categoryBlockName = 'mana_filters/filter_category';
69
- $this->_attributeFilterBlockName = 'mana_filters/filter_attribute';
70
- $this->_priceFilterBlockName = 'mana_filters/filter_price';
71
- $this->_decimalFilterBlockName = 'mana_filters/filter_decimal';
72
- // MANA END
73
- }
74
-
75
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Mana/Filters/Block/View/Search.php DELETED
@@ -1,30 +0,0 @@
1
- <?php
2
- /**
3
- * @category Mana
4
- * @package Mana_Filters
5
- * @copyright Copyright (c) http://www.manadev.com
6
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
- */
8
- /**
9
- * Block type for showing filters in search pages.
10
- * @author Mana Team
11
- * Injected into layout instead of standard catalogsearch/layer in layout XML file.
12
- */
13
- class Mana_Filters_Block_View_Search extends Mage_CatalogSearch_Block_Layer {
14
- /**
15
- * This method is called during page rendering to determine block types to use inside layered navigation block.
16
- * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
17
- * changes are marked with comments.
18
- * @see Mage_CatalogSearch_Block_Layer::_initBlocks()
19
- */
20
- protected function _initBlocks()
21
- {
22
- $this->_stateBlockName = 'catalog/layer_state';
23
- // MANA BEGIN: replace standard block types with ours
24
- $this->_categoryBlockName = 'mana_filters/filter_category';
25
- $this->_attributeFilterBlockName = 'mana_filters/filter_attribute_search';
26
- $this->_priceFilterBlockName = 'mana_filters/filter_price';
27
- $this->_decimalFilterBlockName = 'mana_filters/filter_decimal';
28
- // MANA END
29
- }
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Mana/Filters/Helper/Data.php CHANGED
@@ -47,4 +47,136 @@ class Mana_Filters_Helper_Data extends Mage_Core_Helper_Abstract {
47
  }
48
  return $price;
49
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
47
  }
48
  return $price;
49
  }
50
+
51
+ protected $_filterOptionsCollection;
52
+ protected $_filterSearchOptionsCollection;
53
+ protected $_filterAllOptionsCollection;
54
+ public function getFilterOptionsCollection($allCategories = false) {
55
+ $request = Mage::app()->getRequest();
56
+ if ($request->getModuleName() == 'catalogsearch' && $request->getControllerName() == 'result' && $request->getActionName() == 'index' ||
57
+ $request->getModuleName() == 'manapro_filterajax' && $request->getControllerName() == 'search' && $request->getActionName() == 'index')
58
+ {
59
+ if (!$this->_filterSearchOptionsCollection) {
60
+ $this->_filterSearchOptionsCollection = Mage::getResourceModel('mana_filters/filter2_store_collection')
61
+ ->addColumnToSelect('*')
62
+ ->addStoreFilter(Mage::app()->getStore())
63
+ ->setOrder('position', 'ASC');
64
+ }
65
+ Mage::dispatchEvent('m_before_load_filter_collection', array('collection' => $this->_filterSearchOptionsCollection));
66
+ return $this->_filterSearchOptionsCollection;
67
+ }
68
+ if ($allCategories) {
69
+ if (!$this->_filterAllOptionsCollection) {
70
+ $this->_filterAllOptionsCollection = Mage::getResourceModel('mana_filters/filter2_store_collection')
71
+ ->addColumnToSelect('*')
72
+ ->addStoreFilter(Mage::app()->getStore())
73
+ ->setOrder('position', 'ASC');
74
+ }
75
+ Mage::dispatchEvent('m_before_load_filter_collection', array('collection' => $this->_filterAllOptionsCollection));
76
+ return $this->_filterAllOptionsCollection;
77
+ }
78
+ else {
79
+ if (!$this->_filterOptionsCollection) {
80
+ Mana_Core_Profiler::start('mln', __CLASS__, __METHOD__, '$productCollection->getSetIds()');
81
+ $setIds = Mage::getSingleton('catalog/layer')->getProductCollection()->getSetIds();
82
+ Mana_Core_Profiler::stop('mln', __CLASS__, __METHOD__, '$productCollection->getSetIds()');
83
+ $this->_filterOptionsCollection = Mage::getResourceModel('mana_filters/filter2_store_collection')
84
+ ->addFieldToSelect('*')
85
+ ->addCodeFilter($this->_getAttributeCodes($setIds))
86
+ ->addStoreFilter(Mage::app()->getStore())
87
+ ->setOrder('position', 'ASC');
88
+ }
89
+ Mage::dispatchEvent('m_before_load_filter_collection', array('collection' => $this->_filterOptionsCollection));
90
+ return $this->_filterOptionsCollection;
91
+ }
92
+ }
93
+ protected function _getAttributeCodes($setIds) {
94
+ /* @var $collection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Attribute_Collection */
95
+ $collection = Mage::getResourceModel('catalog/product_attribute_collection');
96
+ $collection->setAttributeSetFilter($setIds);
97
+ $select = $collection->getSelect()
98
+ ->reset(Zend_Db_Select::COLUMNS)
99
+ ->columns('attribute_code');
100
+ return array_merge($collection->getConnection()->fetchCol($select), array('category'));
101
+ }
102
+ public function markLayeredNavigationUrl($url, $routePath, $routeParams) {
103
+ if (Mage::getStoreConfigFlag('mana_filters/session/save_applied_filters')) {
104
+ $routeParams['_nosid'] = true;
105
+ //if (Mage::getSingleton('core/url')->getUrl($routePath, $routeParams) == Mage::getSingleton('core/url')->getRouteUrl($routePath, $routeParams)) {
106
+ $url .= (strpos($url, '?') === false) ? '?m-layered=1' : '&m-layered=1';
107
+ //}
108
+ //else {
109
+ // $url = str_replace('?m-layered=1', '', $url);
110
+ // $url = str_replace('&m-layered=1', '', $url);
111
+ //}
112
+ }
113
+ return $url;
114
+ }
115
+ public function getClearUrl($markUrl = true, $clearListParams = false) {
116
+ $filterState = array();
117
+ foreach (array_merge(Mage::getSingleton('catalog/layer')->getState()->getFilters(), Mage::getSingleton('catalogsearch/layer')->getState()->getFilters()) as $item) {
118
+ $filterState[$item->getFilter()->getRequestVar()] = $item->getFilter()->getCleanValue();
119
+ }
120
+ if ($clearListParams) {
121
+ $filterState = array_merge($filterState, array(
122
+ 'dir' => null,
123
+ 'order' => null,
124
+ 'p' => null,
125
+ 'limit' => null,
126
+ 'mode' => null,
127
+ ));
128
+ }
129
+ $params = array('_secure' => Mage::app()->getFrontController()->getRequest()->isSecure());
130
+ $params['_current'] = true;
131
+ $params['_use_rewrite'] = true;
132
+ $filterState['m-layered'] = null;
133
+ $params['_query'] = $filterState;
134
+ $result = Mage::getUrl('*/*/*', $params);
135
+ if ($markUrl) {
136
+ $result = $this->markLayeredNavigationUrl($result, '*/*/*', $params);
137
+ }
138
+ return $result;
139
+ }
140
+ public function getActiveFilters() {
141
+ $filters = Mage::getSingleton('catalog/layer')->getState()->getFilters();
142
+ if (!is_array($filters)) {
143
+ $filters = array();
144
+ }
145
+ return $filters;
146
+ }
147
+ public function resetProductCollectionWhereClause($select) {
148
+ $preserved = new Varien_Object(array('preserved' => array()));
149
+ $where = $select->getPart(Zend_Db_Select::WHERE);
150
+ Mage::dispatchEvent('m_preserve_product_collection_where_clause', compact('where', 'preserved'));
151
+ $preserved = $preserved->getPreserved();
152
+ if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
153
+ foreach ($where as $key => $condition) {
154
+ if (strpos($condition, 'e.website_id = ') !== false || strpos($condition, '`e`.`website_id` = ') !== false) {
155
+ $preserved[$key] = $key;
156
+ }
157
+ if (strpos($condition, 'e.customer_group_id = ') !== false || strpos($condition, '`e`.`customer_group_id` = ') !== false) {
158
+ $preserved[$key] = $key;
159
+ }
160
+ }
161
+
162
+ }
163
+ foreach ($where as $key => $condition) {
164
+ if (!in_array($key, $preserved)) {
165
+ unset($where[$key]);
166
+ }
167
+ }
168
+ $where = array_values($where);
169
+ if (isset($where[0]) && strpos($where[0], 'AND ') === 0) {
170
+ $where[0] = substr($where[0], strlen('AND '));
171
+ }
172
+ $select->setPart(Zend_Db_Select::WHERE, $where);
173
+ }
174
+ public function getLayer () {
175
+ if (in_array(Mage::helper('mana_core')->getRoutePath(), array('catalogsearch/result/index', 'manapro_filterajax/search/index'))) {
176
+ return Mage::getSingleton('catalogsearch/layer');
177
+ }
178
+ else {
179
+ return Mage::getSingleton('catalog/layer');
180
+ }
181
+ }
182
  }
app/code/local/Mana/Filters/Helper/Extended.php CHANGED
@@ -19,6 +19,7 @@ class Mana_Filters_Helper_Extended extends Mage_Core_Helper_Abstract {
19
  * @return string
20
  */
21
  public function getFilterTemplate($filterBlock) {
 
22
  /* @var $helper Mana_Filters_Helper_Data */ $helper = Mage::helper(strtolower('Mana_Filters'));
23
  $type = $helper->getBlockType($filterBlock);
24
  if (/* @var $displayOptions Mage_Core_Model_Config_Element */ $displayOptions = Mage::getConfig()
@@ -49,74 +50,4 @@ class Mana_Filters_Helper_Extended extends Mage_Core_Helper_Abstract {
49
  Mage::dispatchEvent('mana_filters_process_items', array('filter' => $filter, 'items' => $wrappedItems));
50
  return $wrappedItems->getItems();
51
  }
52
-
53
- /**
54
- * Returns rendered additional markup registered by extensions in configuration under $name key
55
- * @param string $name
56
- * @param array $parameters
57
- * @return string
58
- */
59
- public function getNamedHtml($name, $parameters = array()) {
60
- if (/* @var $markups Mage_Core_Model_Config_Element */ $markups = Mage::getConfig()
61
- ->getNode('mana_filters/markup/'.$name))
62
- {
63
- $templates = array();
64
- foreach ($markups->children() as $name => /* @var $markup Mage_Core_Model_Config_Element */ $markup) {
65
- $templates[] = array('name' => $name, 'position' => (string)$markup->position, 'template' => (string)$markup->template);
66
- }
67
- usort($templates, array('Mana_Filters_Helper_Extended', '_sortByPosition'));
68
- $result = '';
69
- foreach ($templates as $template) {
70
- $filename = Mage::getBaseDir('design').DS.
71
- Mage::getDesign()->getTemplateFilename($template['template'], array('_relative'=>true));
72
- if (file_exists($filename)) {
73
- $result .= $this->_fetchHtml($filename, $parameters);
74
- }
75
- }
76
- return $result;
77
- }
78
- else return '';
79
- }
80
- protected function _fetchHtml($filename, $parameters) {
81
- extract ($parameters, EXTR_OVERWRITE);
82
- ob_start();
83
- try {
84
- include $filename;
85
- }
86
- catch (Exception $e) {
87
- ob_get_clean();
88
- throw $e;
89
- }
90
- return ob_get_clean();
91
- }
92
- public static function _sortByPosition($a, $b) {
93
- if ($a['position'] < $b['position']) return -1;
94
- elseif ($a['position'] > $b['position']) return 1;
95
- else return 0;
96
- }
97
- public function getFilterUrl($route = '', $params = array()) {
98
- $wrappedParams = new Varien_Object;
99
- $wrappedParams->setParams($params);
100
- $wrappedParams->setIsHandled(false);
101
- $wrappedParams->setResult('');
102
- Mage::dispatchEvent('mana_filters_url', array('route' => $route, 'params' => $wrappedParams));
103
- if ($wrappedParams->getIsHandled()) {
104
- return $wrappedParams->getResult();
105
- }
106
- else {
107
- return Mage::getUrl($route, $params);
108
- }
109
- }
110
- public function getPriceRange($index, $range) {
111
- $wrappedParams = new Varien_Object;
112
- $wrappedParams->setIsHandled(false);
113
- $wrappedParams->setResult(array());
114
- Mage::dispatchEvent('mana_filters_price_range', array('index' => $index, 'range' => $range, 'params' => $wrappedParams));
115
- if ($wrappedParams->getIsHandled()) {
116
- return $wrappedParams->getResult();
117
- }
118
- else {
119
- return array('from' => $range * ($index - 1), 'to' => $range * $index);
120
- }
121
- }
122
  }
19
  * @return string
20
  */
21
  public function getFilterTemplate($filterBlock) {
22
+
23
  /* @var $helper Mana_Filters_Helper_Data */ $helper = Mage::helper(strtolower('Mana_Filters'));
24
  $type = $helper->getBlockType($filterBlock);
25
  if (/* @var $displayOptions Mage_Core_Model_Config_Element */ $displayOptions = Mage::getConfig()
50
  Mage::dispatchEvent('mana_filters_process_items', array('filter' => $filter, 'items' => $wrappedItems));
51
  return $wrappedItems->getItems();
52
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  }
app/code/local/Mana/Filters/Model/Config/Display/Default.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Default value provider which gets value from global configuration, depending on filter type
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Config_Display_Default extends Mana_Core_Model_Config_Default {
15
+ public function getDefaultValue($model, $attributeCode, $path) {
16
+ return Mage::getStoreConfig(sprintf($path, $model->getType()));
17
+ }
18
+ }
app/code/local/Mana/Filters/Model/Filter.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Models/DB-backed model */
10
+ /**
11
+ * In-memory model for individual filter settings
12
+ * @author Mana Team
13
+ */
14
+ class Mana_Filters_Model_Filter extends Mana_Core_Model_Eav {
15
+ const ENTITY = 'm_filter';
16
+
17
+ /**
18
+ * Invoked during model creation process, this method associates this model with resource and resource
19
+ * collection classes
20
+ */
21
+ protected function _construct() {
22
+ $this->_init(strtolower('Mana_Filters/Filter'));
23
+ $this->_eventPrefix = self::ENTITY;
24
+ }
25
+ public function getType() {
26
+ if ($this->getCode() == 'category') {
27
+ return 'category';
28
+ }
29
+ elseif ($this->getCode() == 'price') {
30
+ return 'price';
31
+ }
32
+ else {
33
+ if ($this->getResource()->getBackendType($this->getCode()) == 'decimal') {
34
+ return 'decimal';
35
+ }
36
+ else {
37
+ return 'attribute';
38
+ }
39
+ }
40
+ }
41
+ public function getDisplayOptions() {
42
+ return Mage::getConfig()->getNode('mana_filters/display/'.$this->getType().'/'.$this->getDisplay());
43
+ }
44
+ public function getAttribute() {
45
+ if ($this->getCode() == 'category') {
46
+ return null;
47
+ }
48
+ if (!$this->hasData('attribute')) {
49
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
50
+ $collection = Mage::getSingleton('mana_filters/filter_default')->getFilterableAttributes($this->getStoreId());
51
+ $attribute = $core->collectionFind($collection, 'attribute_code', $this->getCode());
52
+ $this->setAttribute($attribute);
53
+ }
54
+ return $this->getData('attribute');
55
+ }
56
+ }
app/code/local/Mana/Filters/Model/Filter/Attribute.php CHANGED
@@ -94,7 +94,7 @@ class Mana_Filters_Model_Filter_Attribute extends Mage_Catalog_Model_Layer_Filte
94
  */
95
  public function getMSelectedValues() {
96
  $values = Mage::app()->getRequest()->getParam($this->_requestVar);
97
- return $values ? explode('_', $values) : array();
98
  }
99
 
100
  /**
@@ -129,11 +129,11 @@ class Mana_Filters_Model_Filter_Attribute extends Mage_Catalog_Model_Layer_Filte
129
  if (Mage::helper('core/string')->strlen($option['value'])) {
130
  // Check filter type
131
  if ($this->_getIsFilterableAttribute($attribute) == self::OPTIONS_ONLY_WITH_RESULTS) {
132
- if (!empty($optionsCount[$option['value']])) {
133
  $data[] = array(
134
  'label' => $option['label'],
135
  'value' => $option['value'],
136
- 'count' => $optionsCount[$option['value']],
137
  // MANA BEGIN: mark each selected item now in memory so we could later mark it
138
  // visually in markup
139
  'm_selected' => in_array($option['value'], $selectedOptionIds),
@@ -160,6 +160,12 @@ class Mana_Filters_Model_Filter_Attribute extends Mage_Catalog_Model_Layer_Filte
160
  );
161
 
162
  $tags = $this->getLayer()->getStateTags($tags);
 
 
 
 
 
 
163
  $this->getLayer()->getAggregator()->saveCacheData($data, $key, $tags);
164
  }
165
  return $data;
@@ -173,8 +179,33 @@ class Mana_Filters_Model_Filter_Attribute extends Mage_Catalog_Model_Layer_Filte
173
  protected function _getResource()
174
  {
175
  if (is_null($this->_resource)) {
176
- $this->_resource = Mage::getResourceModel('mana_filters/filter_attribute');
177
  }
178
  return $this->_resource;
179
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  }
94
  */
95
  public function getMSelectedValues() {
96
  $values = Mage::app()->getRequest()->getParam($this->_requestVar);
97
+ return $values ? array_filter(explode('_', $values)) : array();
98
  }
99
 
100
  /**
129
  if (Mage::helper('core/string')->strlen($option['value'])) {
130
  // Check filter type
131
  if ($this->_getIsFilterableAttribute($attribute) == self::OPTIONS_ONLY_WITH_RESULTS) {
132
+ if (!empty($optionsCount[$option['value']]) || in_array($option['value'], $selectedOptionIds)) {
133
  $data[] = array(
134
  'label' => $option['label'],
135
  'value' => $option['value'],
136
+ 'count' => isset($optionsCount[$option['value']]) ? $optionsCount[$option['value']] : 0,
137
  // MANA BEGIN: mark each selected item now in memory so we could later mark it
138
  // visually in markup
139
  'm_selected' => in_array($option['value'], $selectedOptionIds),
160
  );
161
 
162
  $tags = $this->getLayer()->getStateTags($tags);
163
+ if ($sortMethod = $this->getFilterOptions()->getSortMethod()) {
164
+ foreach ($data as $position => &$item) {
165
+ $item['position'] = $position;
166
+ }
167
+ usort($data, array(Mage::getSingleton('mana_filters/sort'), $sortMethod));
168
+ }
169
  $this->getLayer()->getAggregator()->saveCacheData($data, $key, $tags);
170
  }
171
  return $data;
179
  protected function _getResource()
180
  {
181
  if (is_null($this->_resource)) {
182
+ $this->_resource = Mage::getResourceModel((string)$this->getDisplayOptions()->resource);
183
  }
184
  return $this->_resource;
185
  }
186
+
187
+ protected function _getIsFilterable()
188
+ {
189
+ switch ($this->getMode()) {
190
+ case 'category': return $this->getFilterOptions()->getIsEnabled();
191
+ case 'search': return $this->getFilterOptions()->getIsEnabledInSearch();
192
+ default: throw new Exception('Not implemented');
193
+ }
194
+ }
195
+
196
+ public function getRemoveUrl() {
197
+ $query = array($this->getRequestVar()=>$this->getResetValue());
198
+ $params = array('_secure' => Mage::app()->getFrontController()->getRequest()->isSecure());
199
+ $params['_current'] = true;
200
+ $params['_use_rewrite'] = true;
201
+ $params['_query'] = $query;
202
+ return Mage::helper('mana_filters')->markLayeredNavigationUrl(Mage::getUrl('*/*/*', $params), '*/*/*', $params);
203
+ }
204
+ protected function _getIsFilterableAttribute($attribute)
205
+ {
206
+ return $this->_getIsFilterable(); //return $this->getFilterOptions()->getIsEnabled();
207
+ }
208
+ public function getName() {
209
+ return $this->getFilterOptions()->getName();
210
+ }
211
  }
app/code/local/Mana/Filters/Model/Filter/Attribute/Search.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
- /**
3
- * @category Mana
4
- * @package Mana_Filters
5
- * @copyright Copyright (c) http://www.manadev.com
6
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
- */
8
- class Mana_Filters_Model_Filter_Attribute_Search extends Mana_Filters_Model_Filter_Attribute {
9
- /**
10
- * Check whether specified attribute can be used in LN
11
- *
12
- * @param Mage_Catalog_Model_Resource_Eav_Attribute $attribute
13
- * @return bool
14
- */
15
- protected function _getIsFilterableAttribute($attribute)
16
- {
17
- return $attribute->getIsFilterableInSearch();
18
- }
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Mana/Filters/Model/Filter/Category.php CHANGED
@@ -92,4 +92,71 @@ class Mana_Filters_Model_Filter_Category extends Mage_Catalog_Model_Layer_Filter
92
  $this->_items = $items;
93
  return $this;
94
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  }
92
  $this->_items = $items;
93
  return $this;
94
  }
95
+ protected function _getCategoryItemsData($category, $products, $categories = null)
96
+ {
97
+ if (!$categories) {
98
+ $categories = $category->getChildrenCategories();
99
+ $products->addCountToCategories($categories);
100
+ }
101
+ $data = array();
102
+ foreach ($categories as $category) {
103
+ if ($category->getIsActive() && $category->getProductCount()) {
104
+ $data[] = array(
105
+ 'label' => Mage::helper('core')->htmlEscape($category->getName()),
106
+ 'value' => $category->getId(),
107
+ 'count' => $category->getProductCount(),
108
+ 'm_selected' => $category->getId() == $this->getCategory()->getId()
109
+ );
110
+ }
111
+ }
112
+ return $data;
113
+ }
114
+ protected function _getItemsData()
115
+ {
116
+ $key = $this->getLayer()->getStateKey().'_SUBCATEGORIES';
117
+ $data = $this->getLayer()->getAggregator()->getCacheData($key);
118
+
119
+ if ($data === null) {
120
+ $categoty = $this->getCategory();
121
+ /** @var $categoty Mage_Catalog_Model_Categeory */
122
+ Mana_Core_Profiler::start('mln', __CLASS__, __METHOD__, '$categoty->getChildrenCategories()');
123
+ $categories = $categoty->getChildrenCategories();
124
+ Mana_Core_Profiler::stop('mln', __CLASS__, __METHOD__, '$categoty->getChildrenCategories()');
125
+
126
+ $this->getLayer()->getProductCollection()
127
+ ->addCountToCategories($categories);
128
+
129
+ $data = array();
130
+ foreach ($categories as $category) {
131
+ if ($category->getIsActive() && $category->getProductCount()) {
132
+ $data[] = array(
133
+ 'label' => Mage::helper('core')->htmlEscape($category->getName()),
134
+ 'value' => $category->getId(),
135
+ 'count' => $category->getProductCount(),
136
+ );
137
+ }
138
+ }
139
+ $data = $this->_getCategoryItemsData($this->getCategory(), $this->getLayer()->getProductCollection(), $categories);
140
+ //if (!count($data)) { // no child categories having products
141
+ // $category = $this->getCategory()->getParentCategory();
142
+ // $products = clone ($this->getLayer()->getProductCollection());
143
+ // $products->addCategoryFilter($category);
144
+ // $data = $this->_getCategoryItemsData($category, $products);
145
+ //}
146
+ $tags = $this->getLayer()->getStateTags();
147
+ $this->getLayer()->getAggregator()->saveCacheData($data, $key, $tags);
148
+ }
149
+ return $data;
150
+ }
151
+ public function getRemoveUrl() {
152
+ $query = array($this->getRequestVar()=>$this->getResetValue());
153
+ $params = array('_secure' => Mage::app()->getFrontController()->getRequest()->isSecure());
154
+ $params['_current'] = true;
155
+ $params['_use_rewrite'] = true;
156
+ $params['_query'] = $query;
157
+ return Mage::helper('mana_filters')->markLayeredNavigationUrl(Mage::getUrl('*/*/*', $params), '*/*/*', $params);
158
+ }
159
+ public function getName() {
160
+ return $this->getFilterOptions()->getName();
161
+ }
162
  }
app/code/local/Mana/Filters/Model/Filter/Decimal.php ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * Model type for holding information in memory about possible or applied price type filter
10
+ * @author Mana Team
11
+ * Injected instead of standard catalog/layer_filter_attribute in Mana_Filters_Block_Filter_Price constructor.
12
+ */
13
+ class Mana_Filters_Model_Filter_Decimal extends Mage_Catalog_Model_Layer_Filter_Decimal {
14
+ /**
15
+ * Apply price filter to product collection
16
+ * @param Zend_Controller_Request_Abstract $request
17
+ * @param Mana_Filters_Block_Filter_Price $filterBlock
18
+ * @return Mana_Filters_Model_Filter_Price
19
+ * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
20
+ * changes are marked with comments.
21
+ * @see app/code/core/Mage/Catalog/Model/Layer/Filter/Mage_Catalog_Model_Layer_Filter_Price::apply()
22
+ */
23
+ public function apply(Zend_Controller_Request_Abstract $request, $filterBlock)
24
+ {
25
+ // MANA BEGIN: read multiple price ranges from URL instead of single range read in standard code,
26
+ // apply multiple ranges, show multiple ranges in state and do not hide selected ranges from filter options
27
+ $selections = $this->getMSelectedValues();
28
+
29
+ if (count($selections) > 0) {
30
+ list($index, $range) = explode(',', $selections[0]);
31
+ if ((int)$range) {
32
+ $this->setRange((int)$range);
33
+ $this->_applyToCollectionEx($selections);
34
+ foreach ($selections as $selection) {
35
+ list($index, $range) = explode(',', $selection);
36
+ $this->getLayer()->getState()->addFilter($this->_createItemEx(array(
37
+ 'label' => $this->_renderItemLabel($range, $index),
38
+ 'value' => $selection,
39
+ 'm_selected' => true,
40
+ )));
41
+ }
42
+ }
43
+ // $this->_items = array();
44
+ }
45
+ // MANA END
46
+ return $this;
47
+ }
48
+ /**
49
+ * Prepare text of item label
50
+ *
51
+ * @param int $range
52
+ * @param float $value
53
+ * @return string
54
+ */
55
+ protected function _renderItemLabel($range, $value)
56
+ {
57
+ $range = $this->_getResource()->getRange($value, $range);
58
+ $result = new Varien_Object();
59
+ Mage::dispatchEvent('m_render_price_range', array('range' => $range, 'model' => $this, 'result' => $result));
60
+ if ($result->getLabel()) {
61
+ return $result->getLabel();
62
+ }
63
+ else {
64
+ $store = Mage::app()->getStore();
65
+ $fromPrice = $store->formatPrice($range['from']);
66
+ $toPrice = $store->formatPrice($range['to']);
67
+ return Mage::helper('catalog')->__('%s - %s', $fromPrice, $toPrice);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Applies one or more price filters to currently viewed product collection
73
+ * @param array $selections
74
+ * @return Mana_Filters_Model_Filter_Price
75
+ * This method is cloned from method _applyToCollection() in parent class (method body was pasted from parent class
76
+ * completely rewritten.
77
+ * Standard method did not give us possibility to filter multiple ranges.
78
+ */
79
+ protected function _applyToCollectionEx($selections)
80
+ {
81
+ $this->_getResource()->applyFilterToCollectionEx($this, $selections);
82
+ return $this;
83
+ }
84
+ /**
85
+ * Creates in-memory representation of a single option of a filter
86
+ * @param array $data
87
+ * @return Mana_Filters_Model_Item
88
+ * This method is cloned from method _createItem() in parent class (method body was pasted from parent class
89
+ * completely rewritten.
90
+ * Standard method did not give us possibility to initialize non-standard fields.
91
+ */
92
+ protected function _createItemEx($data)
93
+ {
94
+ return Mage::getModel('mana_filters/item')
95
+ ->setData($data)
96
+ ->setFilter($this);
97
+ }
98
+ /**
99
+ * Initializes internal array of in-memory representations of options of a filter
100
+ * @return Mana_Filters_Model_Filter_Attribute
101
+ * @see Mage_Catalog_Model_Layer_Filter_Abstract::_initItems()
102
+ * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
103
+ * changes are marked with comments.
104
+ */
105
+ protected function _initItems()
106
+ {
107
+ $data = $this->_getItemsData();
108
+ $items=array();
109
+ foreach ($data as $itemData) {
110
+ // MANA BEGIN
111
+ $items[] = $this->_createItemEx($itemData);
112
+ // MANA END
113
+ }
114
+ // MANA BEGIN: enable additional filter item processing
115
+ /* @var $ext Mana_Filters_Helper_Extended */ $ext = Mage::helper(strtolower('Mana_Filters/Extended'));
116
+ $items = $ext->processFilterItems($this, $items);
117
+ // MANA END
118
+ $this->_items = $items;
119
+ return $this;
120
+ }
121
+ /**
122
+ * Returns all values currently selected for this filter
123
+ */
124
+ public function getMSelectedValues() {
125
+ $values = Mage::app()->getRequest()->getParam($this->_requestVar);
126
+ return $values ? explode('_', $values) : array();
127
+ // $result = array();
128
+ // foreach ($values as $value) {
129
+ // list($index, $range) = explode(',', $value);
130
+ // $result[] = array('index' => $index, 'range' => $range);
131
+ // }
132
+ // return $result;
133
+ }
134
+ /**
135
+ * Depending on current filter values, returns available filter options from database
136
+ * and additionally whether individual options are selected or not.
137
+ * @return array
138
+ * @see Mage_Catalog_Model_Layer_Filter_Price::_getItemsData()
139
+ * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
140
+ * changes are marked with comments.
141
+ */
142
+ protected function _getItemsData()
143
+ {
144
+ $range = $this->getRange();
145
+ $dbRanges = $this->getRangeItemCounts($range);
146
+ $data = array();
147
+
148
+ // MANA BEGIN
149
+ $selectedIndexes = array();
150
+ foreach ($this->getMSelectedValues() as $selection) {
151
+ list($index, $range) = explode(',', $selection);
152
+ $selectedIndexes[] = $index;
153
+ }
154
+ // MANA END
155
+
156
+ foreach ($dbRanges as $index=>$count) {
157
+ $data[] = array(
158
+ 'label' => $this->_renderItemLabel($range, $index),
159
+ 'value' => $index . ',' . $range,
160
+ 'count' => $count,
161
+ // MANA BEGIN
162
+ 'm_selected' => in_array($index, $selectedIndexes),
163
+ // MANA END
164
+ );
165
+ }
166
+
167
+ return $data;
168
+ }
169
+ /**
170
+ * This method locates resource type which should do all dirty job with the database. In this override, we
171
+ * instruct Magento to take our resource type, not standard.
172
+ * @see Mage_Catalog_Model_Layer_Filter_Price::_getResource()
173
+ */
174
+ protected function _getResource()
175
+ {
176
+ if (is_null($this->_resource)) {
177
+ $this->_resource = Mage::getResourceModel((string)$this->getDisplayOptions()->resource);
178
+ }
179
+ return $this->_resource;
180
+ }
181
+ public function getLowestPossibleValue() {
182
+ return (int)$this->getMinValue();
183
+ }
184
+ public function getHighestPossibleValue() {
185
+ $result = (int)ceil($this->getMaxValue());
186
+ $min = $this->getLowestPossibleValue();
187
+ return $result != $min ? $result : $result + 1;
188
+ }
189
+ public function getCurrentRangeLowerBound() {
190
+ $selections = $this->getMSelectedValues();
191
+ if ($selections && count($selections) == 1) {
192
+ list($index, $range) = explode(',', $selections[0]);
193
+ return $index;
194
+ }
195
+ else {
196
+ return $this->getLowestPossibleValue();
197
+ }
198
+ }
199
+ public function getCurrentRangeHigherBound() {
200
+ $selections = $this->getMSelectedValues();
201
+ if ($selections && count($selections) == 1) {
202
+ list($index, $range) = explode(',', $selections[0]);
203
+ return $range;
204
+ }
205
+ else {
206
+ return $this->getHighestPossibleValue();
207
+ }
208
+ }
209
+ public function getRemoveUrl() {
210
+ $query = array($this->getRequestVar()=>$this->getResetValue());
211
+ $params = array('_secure' => Mage::app()->getFrontController()->getRequest()->isSecure());
212
+ $params['_current'] = true;
213
+ $params['_use_rewrite'] = true;
214
+ $params['_query'] = $query;
215
+ return Mage::helper('mana_filters')->markLayeredNavigationUrl(Mage::getUrl('*/*/*', $params), '*/*/*', $params);
216
+ }
217
+ public function getName() {
218
+ return $this->getFilterOptions()->getName();
219
+ }
220
+ public function getRange() {
221
+ $range = $this->getData('range');
222
+ if (!$range) {
223
+ if (Mage::helper('mana_db')->hasOverriddenValueEx($this->getFilterOptions(), 24)) {
224
+ $range = (float)$this->getFilterOptions()->getRangeStep();
225
+ }
226
+ elseif (Mage::helper('mana_db')->hasOverriddenValueEx($this->getFilterOptions(), 24, 'global_default_mask')) {
227
+ $range = (float)$this->getFilterOptions()->getGlobalRangeStep();
228
+ }
229
+ }
230
+ if (!$range) {
231
+ $maxValue = $this->getMaxValue();
232
+ $index = 1;
233
+ do {
234
+ $range = pow(10, (strlen(floor($maxValue)) - $index));
235
+ $items = $this->getRangeItemCounts($range);
236
+ $index++;
237
+ }
238
+ while ($range > self::MIN_RANGE_POWER && count($items) < 2);
239
+ $this->setData('range', $range);
240
+ }
241
+
242
+ return $range;
243
+ }
244
+ }
app/code/local/Mana/Filters/Model/Filter/Default.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Default value provider which gets value from filterable attribute
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Filter_Default {
15
+ protected $_filterableAttributes;
16
+ public function getFilterableAttributes($storeId) {
17
+ if (!$this->_filterableAttributes) {
18
+ $this->_filterableAttributes = array();
19
+ }
20
+ if (!isset($this->_filterableAttributes[$storeId])) {
21
+ $attributes = Mage::getResourceModel('catalog/product_attribute_collection')
22
+ ->addIsFilterableFilter()
23
+ ->setItemObjectClass('catalog/resource_eav_attribute')
24
+ ->addStoreLabel($storeId);
25
+ $attributes->getSelect()->distinct(true);
26
+ $this->_filterableAttributes[$storeId] = $attributes;
27
+ }
28
+ return $this->_filterableAttributes[$storeId];
29
+ }
30
+ public function getDefaultValue($model, $attributeCode, $source) {
31
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
32
+ if (!($attribute = $core->collectionFind($this->getFilterableAttributes($model->getStoreId()), 'attribute_code', $model->getCode()))) {
33
+ /* @var $attribute Mage_Catalog_Model_Entity_Attribute */ $attribute = Mage::getModel('catalog/entity_attribute')
34
+ ->loadByCode(Mage_Catalog_Model_Product::ENTITY, $model->getCode());
35
+ if ($attribute->isObjectNew()) {
36
+ switch ($model->getCode()) {
37
+ case 'category': $attribute->setIsFilterable(1)->setIsFilterableInSearch(1)->setPosition(-1)->setStoreLabel(Mage::helper('mana_filters')->__('Category')); break;
38
+ default: throw new Exception(Mage::helper('mana_filters')->__('Attribute %s not found.', $model->getCode()));
39
+ }
40
+ }
41
+ }
42
+ switch ($attributeCode) {
43
+ case 'name': return $attribute->getStoreLabel();
44
+ case 'is_enabled': return $attribute->getIsFilterable() ? 1 : 0;
45
+ case 'is_enabled_in_search': return $attribute->getIsFilterableInSearch() ? 1 : 0;
46
+ case 'position': return $attribute->getPosition();
47
+ default: throw new Exception('Not implemented');
48
+ }
49
+ }
50
+ public function getUseDefaultLabel() {
51
+ return Mage::helper('mana_filters')->__('Use Product Attribute');
52
+ }
53
+ }
app/code/local/Mana/Filters/Model/Filter/Price.php CHANGED
@@ -27,19 +27,23 @@ class Mana_Filters_Model_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Pr
27
  $selections = $this->getMSelectedValues();
28
 
29
  if (count($selections) > 0) {
30
- list($index, $range) = explode(',', $selections[0]);
31
- if ((int)$range) {
32
- $this->setPriceRange((int)$range);
33
- $this->_applyToCollectionEx($selections);
34
- foreach ($selections as $selection) {
35
- list($index, $range) = explode(',', $selection);
36
- $this->getLayer()->getState()->addFilter($this->_createItemEx(array(
37
- 'label' => $this->_renderItemLabel($range, $index),
38
- 'value' => $selection,
39
- 'm_selected' => true,
40
- )));
41
- }
42
- }
 
 
 
 
43
  // $this->_items = array();
44
  }
45
  // MANA END
@@ -54,12 +58,18 @@ class Mana_Filters_Model_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Pr
54
  */
55
  protected function _renderItemLabel($range, $value)
56
  {
57
- /* @var $ext Mana_Filters_Helper_Extended */ $ext = Mage::helper(strtolower('Mana_Filters/Extended'));
58
- $range = $ext->getPriceRange($value, $range);
59
- $store = Mage::app()->getStore();
60
- $fromPrice = $store->formatPrice($range['from']);
61
- $toPrice = $store->formatPrice($range['to']);
62
- return Mage::helper('catalog')->__('%s - %s', $fromPrice, $toPrice);
 
 
 
 
 
 
63
  }
64
 
65
  /**
@@ -117,7 +127,7 @@ class Mana_Filters_Model_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Pr
117
  */
118
  public function getMSelectedValues() {
119
  $values = Mage::app()->getRequest()->getParam($this->_requestVar);
120
- return $values ? explode('_', $values) : array();
121
  // $result = array();
122
  // foreach ($values as $value) {
123
  // list($index, $range) = explode(',', $value);
@@ -142,8 +152,10 @@ class Mana_Filters_Model_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Pr
142
  // MANA BEGIN
143
  $selectedIndexes = array();
144
  foreach ($this->getMSelectedValues() as $selection) {
145
- list($index, $range) = explode(',', $selection);
146
- $selectedIndexes[] = $index;
 
 
147
  }
148
  // MANA END
149
 
@@ -160,7 +172,55 @@ class Mana_Filters_Model_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Pr
160
 
161
  return $data;
162
  }
163
- /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  * This method locates resource type which should do all dirty job with the database. In this override, we
165
  * instruct Magento to take our resource type, not standard.
166
  * @see Mage_Catalog_Model_Layer_Filter_Price::_getResource()
@@ -168,7 +228,7 @@ class Mana_Filters_Model_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Pr
168
  protected function _getResource()
169
  {
170
  if (is_null($this->_resource)) {
171
- $this->_resource = Mage::getResourceModel('mana_filters/filter_price');
172
  }
173
  return $this->_resource;
174
  }
@@ -176,7 +236,7 @@ class Mana_Filters_Model_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Pr
176
  return 0;
177
  }
178
  public function getHighestPossibleValue() {
179
- return $this->getMaxPriceInt() + 1;
180
  }
181
  public function getCurrentRangeLowerBound() {
182
  $selections = $this->getMSelectedValues();
@@ -198,4 +258,27 @@ class Mana_Filters_Model_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Pr
198
  return $this->getHighestPossibleValue();
199
  }
200
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  }
27
  $selections = $this->getMSelectedValues();
28
 
29
  if (count($selections) > 0) {
30
+ if (strpos($selections[0], ',') !== false) {
31
+ list($index, $range) = explode(',', $selections[0]);
32
+ if ((int)$range) {
33
+ $this->setPriceRange((int)$range);
34
+ $this->_applyToCollectionEx($selections);
35
+ foreach ($selections as $selection) {
36
+ if (strpos($selection, ',') !== false) {
37
+ list($index, $range) = explode(',', $selection);
38
+ $this->getLayer()->getState()->addFilter($this->_createItemEx(array(
39
+ 'label' => $this->_renderItemLabel($range, $index),
40
+ 'value' => $selection,
41
+ 'm_selected' => true,
42
+ )));
43
+ }
44
+ }
45
+ }
46
+ }
47
  // $this->_items = array();
48
  }
49
  // MANA END
58
  */
59
  protected function _renderItemLabel($range, $value)
60
  {
61
+ $range = $this->_getResource()->getPriceRange($value, $range);
62
+ $result = new Varien_Object();
63
+ Mage::dispatchEvent('m_render_price_range', array('range' => $range, 'model' => $this, 'result' => $result));
64
+ if ($result->getLabel()) {
65
+ return $result->getLabel();
66
+ }
67
+ else {
68
+ $store = Mage::app()->getStore();
69
+ $fromPrice = $store->formatPrice($range['from'], false);
70
+ $toPrice = $store->formatPrice($range['to'], false);
71
+ return Mage::helper('catalog')->__('%s - %s', $fromPrice, $toPrice);
72
+ }
73
  }
74
 
75
  /**
127
  */
128
  public function getMSelectedValues() {
129
  $values = Mage::app()->getRequest()->getParam($this->_requestVar);
130
+ return $values ? explode('_', urldecode($values)) : array();
131
  // $result = array();
132
  // foreach ($values as $value) {
133
  // list($index, $range) = explode(',', $value);
152
  // MANA BEGIN
153
  $selectedIndexes = array();
154
  foreach ($this->getMSelectedValues() as $selection) {
155
+ if (strpos($selection, ',') !== false) {
156
+ list($index, $range) = explode(',', $selection);
157
+ $selectedIndexes[] = $index;
158
+ }
159
  }
160
  // MANA END
161
 
172
 
173
  return $data;
174
  }
175
+ public function getPriceRange()
176
+ {
177
+ $range = $this->getData('price_range');
178
+ if (!$range) {
179
+ if (Mage::helper('mana_db')->hasOverriddenValueEx($this->getFilterOptions(), 24)) {
180
+ $range = (float)$this->getFilterOptions()->getRangeStep();
181
+ }
182
+ elseif (Mage::helper('mana_db')->hasOverriddenValueEx($this->getFilterOptions(), 24, 'global_default_mask')) {
183
+ $range = (float)$this->getFilterOptions()->getGlobalRangeStep();
184
+ }
185
+ }
186
+ if (!$range) {
187
+ $currentCategory = Mage::registry('current_category_filter');
188
+ if ($currentCategory) {
189
+ $range = $currentCategory->getFilterPriceRange();
190
+ } else {
191
+ $range = $this->getLayer()->getCurrentCategory()->getFilterPriceRange();
192
+ }
193
+
194
+ $maxPrice = $this->getMaxPriceInt();
195
+ if (!$range) {
196
+ $calculation = Mage::app()->getStore()->getConfig('catalog/layered_navigation/price_range_calculation');
197
+ if (!$calculation) {
198
+ $calculation = 'auto';
199
+ }
200
+ if ($calculation == 'auto') {
201
+ $index = 1;
202
+ do {
203
+ $range = pow(10, (strlen(floor($maxPrice)) - $index));
204
+ $items = $this->getRangeItemCounts($range);
205
+ $index++;
206
+ }
207
+ while($range > self::MIN_RANGE_POWER && count($items) < 2);
208
+
209
+
210
+ while (ceil($maxPrice / $range) > 25) {
211
+ $range *= 10;
212
+ }
213
+ } else {
214
+ $range = Mage::app()->getStore()->getConfig('catalog/layered_navigation/price_range_step');
215
+ }
216
+ }
217
+
218
+ $this->setData('price_range', $range);
219
+ }
220
+
221
+ return $range;
222
+ }
223
+ /**
224
  * This method locates resource type which should do all dirty job with the database. In this override, we
225
  * instruct Magento to take our resource type, not standard.
226
  * @see Mage_Catalog_Model_Layer_Filter_Price::_getResource()
228
  protected function _getResource()
229
  {
230
  if (is_null($this->_resource)) {
231
+ $this->_resource = Mage::getResourceModel((string)$this->getDisplayOptions()->resource);
232
  }
233
  return $this->_resource;
234
  }
236
  return 0;
237
  }
238
  public function getHighestPossibleValue() {
239
+ return (int)ceil($this->getMaxPriceInt());
240
  }
241
  public function getCurrentRangeLowerBound() {
242
  $selections = $this->getMSelectedValues();
258
  return $this->getHighestPossibleValue();
259
  }
260
  }
261
+ public function getRemoveUrl() {
262
+ $query = array($this->getRequestVar()=>$this->getResetValue());
263
+ $params = array('_secure' => Mage::app()->getFrontController()->getRequest()->isSecure());
264
+ $params['_current'] = true;
265
+ $params['_use_rewrite'] = true;
266
+ $params['_query'] = $query;
267
+ return Mage::helper('mana_filters')->markLayeredNavigationUrl(Mage::getUrl('*/*/*', $params), '*/*/*', $params);
268
+ }
269
+ public function getName() {
270
+ return $this->getFilterOptions()->getName();
271
+ }
272
+ public function getMaxPriceInt() {
273
+ $maxPrice = $this->getData('max_price_int');
274
+ if (is_null($maxPrice)) {
275
+ $maxPrice = $this->_getResource()->getMaxPrice($this);
276
+ $maxPrice = ceil($maxPrice);
277
+ $this->setData('max_price_int', $maxPrice);
278
+ }
279
+ return $maxPrice;
280
+ }
281
+ public function getDecimalDigits() {
282
+ return 0;
283
+ }
284
  }
app/code/local/Mana/Filters/Model/Filter2.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Models/DB-backed model */
10
+ /**
11
+ * INSERT HERE: what is this model for
12
+ * @author Mana Team
13
+ */
14
+ class Mana_Filters_Model_Filter2 extends Mana_Db_Model_Object {
15
+ /**
16
+ * Invoked during model creation process, this method associates this model with resource and resource
17
+ * collection classes
18
+ */
19
+ protected function _construct() {
20
+ $this->_init(strtolower('Mana_Filters/Filter2'));
21
+ }
22
+ public function getDisplayOptions() {
23
+ return Mage::getConfig()->getNode('mana_filters/display/'.$this->getType().'/'.$this->getDisplay());
24
+ }
25
+ public function getAttribute() {
26
+ if ($this->getCode() == 'category') {
27
+ return null;
28
+ }
29
+ if (!$this->hasData('attribute')) {
30
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
31
+ $collection = Mage::getSingleton('mana_filters/filter_default')->getFilterableAttributes($this->getStoreId());
32
+ $attribute = $core->collectionFind($collection, 'attribute_code', $this->getCode());
33
+ $this->setAttribute($attribute);
34
+ }
35
+ return $this->getData('attribute');
36
+ }
37
+
38
+ protected function _validate($result) {
39
+ $t = Mage::helper('mana_filters');
40
+ if (trim($this->getIsEnabled()) === '') {
41
+ $result->addError($t->__('Please fill in %s field', $t->__('In Category')));
42
+ }
43
+ if (trim($this->getDisplay()) === '') {
44
+ $result->addError($t->__('Please fill in %s field', $t->__('Display As')));
45
+ }
46
+ if (trim($this->getName()) === '') {
47
+ $result->addError($t->__('Please fill in %s field', $t->__('Name')));
48
+ }
49
+ if (trim($this->getIsEnabledInSearch()) === '') {
50
+ $result->addError($t->__('Please fill in %s field', $t->__('In Search')));
51
+ }
52
+ if (trim($this->getPosition()) === '') {
53
+ $result->addError($t->__('Please fill in %s field', $t->__('Position')));
54
+ }
55
+ }
56
+ public function getCode() {
57
+ return isset($this->_data['code']) ? $this->_data['code'] : null;
58
+ }
59
+ }
app/code/local/Mana/Filters/Model/Filter2/Store.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Models/DB-backed model */
10
+ /**
11
+ * INSERT HERE: what is this model for
12
+ * @author Mana Team
13
+ */
14
+ class Mana_Filters_Model_Filter2_Store extends Mana_Filters_Model_Filter2 {
15
+ /**
16
+ * Invoked during model creation process, this method associates this model with resource and resource
17
+ * collection classes
18
+ */
19
+ protected function _construct() {
20
+ $this->_init(strtolower('Mana_Filters/Filter2_Store'));
21
+ }
22
+ public function getSliderNumberFormat() {
23
+ return $this->_getCurrentNumberFormat('slider_number_format');
24
+ }
25
+ public function getSliderNumberFormat2() {
26
+ return $this->_getCurrentNumberFormat('slider_number_format2');
27
+ }
28
+ protected function _getCurrentNumberFormat($field) {
29
+ $result = $this->getData($field);
30
+ $store = Mage::app()->getStore();
31
+ if ($result == '$0') {
32
+ return $store->getCurrentCurrency()->formatPrecision(0, 0, array(), false, false);
33
+ }
34
+ else {
35
+ return $result;
36
+ }
37
+ }
38
+ }
app/code/local/Mana/Filters/Model/Filter2/Value.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Models/DB-backed model */
10
+ /**
11
+ * INSERT HERE: what is this model for
12
+ * @author Mana Team
13
+ */
14
+ class Mana_Filters_Model_Filter2_Value extends Mana_Db_Model_Object {
15
+ /**
16
+ * Invoked during model creation process, this method associates this model with resource and resource
17
+ * collection classes
18
+ */
19
+ protected function _construct() {
20
+ $this->_init(strtolower('Mana_Filters/Filter2_Value'));
21
+ }
22
+ }
app/code/local/Mana/Filters/Model/Filter2/Value/Store.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Models/DB-backed model */
10
+ /**
11
+ * INSERT HERE: what is this model for
12
+ * @author Mana Team
13
+ */
14
+ class Mana_Filters_Model_Filter2_Value_Store extends Mana_Filters_Model_Filter2_Value {
15
+ /**
16
+ * Invoked during model creation process, this method associates this model with resource and resource
17
+ * collection classes
18
+ */
19
+ protected function _construct() {
20
+ $this->_init(strtolower('Mana_Filters/Filter2_Value_Store'));
21
+ }
22
+ }
app/code/local/Mana/Filters/Model/Item.php CHANGED
@@ -24,8 +24,8 @@ class Mana_Filters_Model_Item extends Mage_Catalog_Model_Layer_Filter_Item {
24
  public function getUrl()
25
  {
26
  // MANA BEGIN: add multivalue filter handling
27
- /* @var $ext Mana_Filters_Helper_Extended */ $ext = Mage::helper(strtolower('Mana_Filters/Extended'));
28
  $values = $this->getFilter()->getMSelectedValues(); // this could fail if called from some kind of standard filter
 
29
  if (!in_array($this->getValue(), $values)) $values[] = $this->getValue();
30
  // MANA END
31
 
@@ -35,9 +35,33 @@ class Mana_Filters_Model_Item extends Mage_Catalog_Model_Layer_Filter_Item {
35
  // MANA_END
36
  Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls
37
  );
38
- return $ext->getFilterUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true, '_query'=>$query));
 
39
  }
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  /**
42
  * Returns URL which should be loaded if person chooses to remove this filter item from active filters
43
  * @return string
@@ -48,8 +72,12 @@ class Mana_Filters_Model_Item extends Mage_Catalog_Model_Layer_Filter_Item {
48
  public function getRemoveUrl()
49
  {
50
  // MANA BEGIN: add multivalue filter handling
51
- /* @var $ext Mana_Filters_Helper_Extended */ $ext = Mage::helper(strtolower('Mana_Filters/Extended'));
 
 
 
52
  $values = $this->getFilter()->getMSelectedValues(); // this could fail if called from some kind of standard filter
 
53
  unset($values[array_search($this->getValue(), $values)]);
54
  if (count($values) > 0) {
55
  $query = array(
@@ -61,15 +89,24 @@ class Mana_Filters_Model_Item extends Mage_Catalog_Model_Layer_Filter_Item {
61
  $query = array($this->getFilter()->getRequestVar()=>$this->getFilter()->getResetValue());
62
  }
63
  // MANA END
64
-
65
  $params['_current'] = true;
66
  $params['_use_rewrite'] = true;
67
  $params['_query'] = $query;
68
- $params['_escape'] = true;
69
- return $ext->getFilterUrl('*/*/*', $params);
70
  }
71
  public function getUniqueId() {
72
  /* @var $helper Mana_Filters_Helper_Data */ $helper = Mage::helper(strtolower('Mana_Filters'));
73
  return 'filter_'.$helper->getFilterName($this->getFilter()).'_'.$this->getValue();
74
  }
 
 
 
 
 
 
 
 
 
 
75
  }
24
  public function getUrl()
25
  {
26
  // MANA BEGIN: add multivalue filter handling
 
27
  $values = $this->getFilter()->getMSelectedValues(); // this could fail if called from some kind of standard filter
28
+ if (!$values) $values = array();
29
  if (!in_array($this->getValue(), $values)) $values[] = $this->getValue();
30
  // MANA END
31
 
35
  // MANA_END
36
  Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls
37
  );
38
+ $params = array('_current'=>true, '_use_rewrite'=>true, '_query'=>$query, '_secure' => Mage::app()->getFrontController()->getRequest()->isSecure());
39
+ return Mage::helper('mana_filters')->markLayeredNavigationUrl(Mage::getUrl('*/*/*', $params), '*/*/*', $params);
40
  }
41
 
42
+ /**
43
+ * Returns URL which should be loaded if person chooses to add this filter item into active filters
44
+ * @return string
45
+ * @see Mage_Catalog_Model_Layer_Filter_Item::getUrl()
46
+ * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
47
+ * changes are marked with comments.
48
+ */
49
+ public function getReplaceUrl()
50
+ {
51
+ // MANA BEGIN: add multivalue filter handling
52
+ $values = array();
53
+ if (!in_array($this->getValue(), $values)) $values[] = $this->getValue();
54
+ // MANA END
55
+
56
+ $query = array(
57
+ // MANA BEGIN: save multiple values in URL as concatenated with '_'
58
+ $this->getFilter()->getRequestVar()=>implode('_', $values),
59
+ // MANA_END
60
+ Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls
61
+ );
62
+ $params = array('_current'=>true, '_use_rewrite'=>true, '_query'=>$query, '_secure' => Mage::app()->getFrontController()->getRequest()->isSecure());
63
+ return Mage::helper('mana_filters')->markLayeredNavigationUrl(Mage::getUrl('*/*/*', $params), '*/*/*', $params);
64
+ }
65
  /**
66
  * Returns URL which should be loaded if person chooses to remove this filter item from active filters
67
  * @return string
72
  public function getRemoveUrl()
73
  {
74
  // MANA BEGIN: add multivalue filter handling
75
+ if ($this->hasData('remove_url')) {
76
+ return $this->getData('remove_url');
77
+ }
78
+
79
  $values = $this->getFilter()->getMSelectedValues(); // this could fail if called from some kind of standard filter
80
+ if (!$values) $values = array();
81
  unset($values[array_search($this->getValue(), $values)]);
82
  if (count($values) > 0) {
83
  $query = array(
89
  $query = array($this->getFilter()->getRequestVar()=>$this->getFilter()->getResetValue());
90
  }
91
  // MANA END
92
+ $params = array('_secure' => Mage::app()->getFrontController()->getRequest()->isSecure());
93
  $params['_current'] = true;
94
  $params['_use_rewrite'] = true;
95
  $params['_query'] = $query;
96
+ return Mage::helper('mana_filters')->markLayeredNavigationUrl(Mage::getUrl('*/*/*', $params), '*/*/*', $params);
 
97
  }
98
  public function getUniqueId() {
99
  /* @var $helper Mana_Filters_Helper_Data */ $helper = Mage::helper(strtolower('Mana_Filters'));
100
  return 'filter_'.$helper->getFilterName($this->getFilter()).'_'.$this->getValue();
101
  }
102
+
103
+ public function getSeoValue() {
104
+ $urlValue = $this->getValue();
105
+ if (((string)Mage::getConfig()->getNode('modules/ManaPro_FilterSeoLinks/active')) == 'true') {
106
+ $url = Mage::getModel('manapro_filterseolinks/url');
107
+ /* @var $url ManaPro_FilterSeoLinks_Model_Url */
108
+ $urlValue = $url->encodeValue($this->getFilter()->getRequestVar(), $urlValue);
109
+ }
110
+ return $urlValue;
111
+ }
112
  }
app/code/local/Mana/Filters/Model/Observer.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Models/Observer */
10
+ /**
11
+ * This class observes certain (defined in etc/config.xml) events in the whole system and provides public methods - handlers for
12
+ * these events.
13
+ * @author Mana Team
14
+ *
15
+ */
16
+ class Mana_Filters_Model_Observer {
17
+ /* BASED ON SNIPPET: Models/Event handler */
18
+ /**
19
+ * Raises flag is config value changed this module's replicated tables rely on (handles event "m_db_is_config_changed")
20
+ * @param Varien_Event_Observer $observer
21
+ */
22
+ public function isConfigChanged($observer) {
23
+ /* @var $result Varien_Object */ $result = $observer->getEvent()->getResult();
24
+ /* @var $configData Mage_Core_Model_Config_Data */ $configData = $observer->getEvent()->getConfigData();
25
+
26
+ Mage::helper('mana_db')->checkIfPathsChanged($result, $configData, array(
27
+ 'mana_filters/display/attribute',
28
+ 'mana_filters/display/price',
29
+ 'mana_filters/display/category',
30
+ 'mana_filters/display/decimal',
31
+ 'mana_filters/display/sort_method',
32
+ ));
33
+ }
34
+ /**
35
+ * REPLACE THIS WITH DESCRIPTION (handles event "catalog_entity_attribute_save_commit_after")
36
+ * @param Varien_Event_Observer $observer
37
+ */
38
+ public function afterCatalogAttributeSave($observer) {
39
+ $dataObject = $observer->getEvent()->getDataObject();
40
+
41
+ if (!$dataObject->getdata('_m_prevent_replication') && $dataObject->getIsFilterable() == 0) {
42
+ $filter = Mage::getModel('mana_filters/filter2')->load($dataObject->getAttributeCode(), 'code');
43
+ if ($filter->getId()) {
44
+ $filter->delete();
45
+ }
46
+ }
47
+ }
48
+ }
app/code/local/Mana/Filters/Model/Operation.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://www.manadev.com/license Proprietary License
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Filters_Model_Operation extends Mana_Core_Model_Source_Abstract {
13
+ protected function _getAllOptions() {
14
+ return array(
15
+ array('value' => '', 'label' => Mage::helper('mana_filters')->__('Logical OR')),
16
+ array('value' => 'and', 'label' => Mage::helper('mana_filters')->__('Logical AND')),
17
+ );
18
+ }
19
+ }
app/code/local/Mana/Filters/Model/Sort.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://www.manadev.com/license Proprietary License
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ *
11
+ */
12
+ class Mana_Filters_Model_Sort extends Mana_Core_Model_Source_Abstract {
13
+ protected function _bySelected($a, $b) {
14
+ if (!$a['m_selected'] && $b['m_selected']) return 1;
15
+ if ($a['m_selected'] && !$b['m_selected']) return -1;
16
+ return 0;
17
+ }
18
+ public function byPosition($a, $b) {
19
+ if ($a['position'] < $b['position']) return -1;
20
+ if ($a['position'] > $b['position']) return 1;
21
+ return 0;
22
+ }
23
+ public function bySelected($a, $b) {
24
+ $result = $this->_bySelected($a, $b);
25
+ return $result != 0 ? $result : $this->byPosition($a, $b);
26
+ }
27
+ public function byName($a, $b) {
28
+ if ($a['label'] < $b['label']) return -1;
29
+ if ($a['label'] > $b['label']) return 1;
30
+ return 0;
31
+ }
32
+ public function bySelectedName($a, $b) {
33
+ $result = $this->_bySelected($a, $b);
34
+ return $result != 0 ? $result : $this->byName($a, $b);
35
+ }
36
+ public function byCount($a, $b) {
37
+ if ($a['count'] < $b['count']) return 1;
38
+ if ($a['count'] > $b['count']) return -1;
39
+ return 0;
40
+ }
41
+ public function bySelectedCount($a, $b) {
42
+ $result = $this->_bySelected($a, $b);
43
+ return $result != 0 ? $result : $this->byCount($a, $b);
44
+ }
45
+ protected function _getAllOptions() {
46
+ return array(
47
+ array('value' => '', 'label' => Mage::helper('mana_filters')->__('Position')),
48
+ array('value' => 'bySelected', 'label' => Mage::helper('mana_filters')->__('Position (selected at the top)')),
49
+ array('value' => 'byName', 'label' => Mage::helper('mana_filters')->__('Name')),
50
+ array('value' => 'bySelectedName', 'label' => Mage::helper('mana_filters')->__('Name (selected at the top)')),
51
+ array('value' => 'byCount', 'label' => Mage::helper('mana_filters')->__('Count')),
52
+ array('value' => 'bySelectedCount', 'label' => Mage::helper('mana_filters')->__('Count (selected at the top)')),
53
+ );
54
+ }
55
+ }
app/code/local/Mana/Filters/Model/Source/Display.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Base class for sources of filter templates
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Source_Display extends Mana_Core_Model_Source_Abstract {
15
+ protected $_filterType = ''; // this should be filled in derived classes
16
+
17
+ protected function _getAllOptions() {
18
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
19
+ $result = array();
20
+
21
+ foreach ($core->getSortedXmlChildren(Mage::getConfig()->getNode('mana_filters/display'), $this->_filterType) as $key => $options) {
22
+ $module = isset($options['module']) ? ((string)$options['module']) : 'manapro_filteradmin';
23
+ $result[] = array('label' => Mage::helper($module)->__((string)$options->title), 'value' => $key);
24
+ }
25
+ return $result;
26
+ }
27
+ public function getDbType() {
28
+ return 'varchar(255)';
29
+ }
30
+ public function getDbDefaultValue() {
31
+ return '';
32
+ }
33
+ }
app/code/local/Mana/Filters/Model/Source/Display/All.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Source of all registered filter templates, regardless their type
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Source_Display_All extends Mana_Core_Model_Source_Abstract {
15
+ protected function _getAllOptions() {
16
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
17
+ $result = array();
18
+
19
+ foreach (array('attribute', 'price', 'category', 'decimal') as $filterType) {
20
+ foreach ($core->getSortedXmlChildren(Mage::getConfig()->getNode('mana_filters/display'), $filterType) as $key => $options) {
21
+ $found = false;
22
+ foreach ($result as $item) {
23
+ if ($item['value'] == $key) {
24
+ $found = true;
25
+ break;
26
+ }
27
+ }
28
+
29
+ if (!$found) {
30
+ $module = isset($options['module']) ? ((string)$options['module']) : 'manapro_filteradmin';
31
+ $result[] = array('label' => Mage::helper($module)->__((string)$options->title), 'value' => $key);
32
+ }
33
+ }
34
+ }
35
+ usort($result, array($this, '_compareOptions'));
36
+ return $result;
37
+ }
38
+
39
+ public function _compareOptions($a, $b) {
40
+ if ($a['label'] == $b['label']) return 0;
41
+ return mb_convert_case($a['label'], MB_CASE_UPPER, "UTF-8") < mb_convert_case($b['label'], MB_CASE_UPPER, "UTF-8") ? -1 : 1;
42
+ }
43
+ }
app/code/local/Mana/Filters/Model/Source/Display/Attribute.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Source attribute filter templates
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Source_Display_Attribute extends Mana_Filters_Model_Source_Display {
15
+ protected $_filterType = 'attribute';
16
+ }
app/code/local/Mana/Filters/Model/Source/Display/Category.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Source of category filter templates
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Source_Display_Category extends Mana_Filters_Model_Source_Display {
15
+ protected $_filterType = 'category';
16
+ }
app/code/local/Mana/Filters/Model/Source/Display/Decimal.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Category of price filter templates
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Source_Display_Decimal extends Mana_Filters_Model_Source_Display {
15
+ protected $_filterType = 'decimal';
16
+ }
app/code/local/Mana/Filters/Model/Source/Display/Price.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Category of price filter templates
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Source_Display_Price extends Mana_Filters_Model_Source_Display {
15
+ protected $_filterType = 'price';
16
+ }
app/code/local/Mana/Filters/Model/Source/Filterable.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Source for options of filter being filterable
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Model_Source_Filterable extends Mana_Core_Model_Source_Abstract {
15
+ protected function _getAllOptions() {
16
+ return array(
17
+ array('value' => '0', 'label' => Mage::helper('catalog')->__('No')),
18
+ array('value' => '1', 'label' => Mage::helper('catalog')->__('Filterable (with results)')),
19
+ array('value' => '2', 'label' => Mage::helper('catalog')->__('Filterable (no results)')),
20
+ );
21
+ }
22
+ public function getDbType() {
23
+ return 'tinyint';
24
+ }
25
+ public function getDbDefaultValue() {
26
+ return 0;
27
+ }
28
+ }
app/code/local/Mana/Filters/Resource/Filter.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Resource for extracting filter settings for specified store.
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Resource_Filter extends Mana_Core_Resource_Eav {
15
+ /**
16
+ * This method is invoked from constructor to setup resource against database
17
+ */
18
+ protected function _construct() {
19
+ parent::_construct();
20
+ $this->setType('m_filter');
21
+ }
22
+ public function getBackendType($attributeCode) {
23
+ return $this->_getReadAdapter()->fetchOne("SELECT backend_type FROM `{$this->getTable('eav/attribute')}` WHERE `attribute_code` = '$attributeCode'");
24
+ }
25
+ }
app/code/local/Mana/Filters/Resource/Filter/Attribute.php CHANGED
@@ -31,22 +31,43 @@ class Mana_Filters_Resource_Filter_Attribute extends Mage_Catalog_Model_Resource
31
 
32
  $attribute = $filter->getAttributeModel();
33
  $connection = $this->_getReadAdapter();
34
- $tableAlias = $attribute->getAttributeCode() . '_idx';
35
- $conditions = array(
36
- "{$tableAlias}.entity_id = e.entity_id",
37
- $connection->quoteInto("{$tableAlias}.attribute_id = ?", $attribute->getAttributeId()),
38
- $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId()),
39
- // MANA BEGIN: apply multiple values for filtered field
40
- "{$tableAlias}.value in (".implode(',', explode('_', $value)).")"
41
- // $connection->quoteInto("{$tableAlias}.value = ?", $value)
42
- // MANA END
43
- );
44
-
45
- $collection->getSelect()->join(
46
- array($tableAlias => $this->getMainTable()),
47
- join(' AND ', $conditions),
48
- array()
49
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  return $this;
52
  }
@@ -99,7 +120,7 @@ class Mana_Filters_Resource_Filter_Attribute extends Mage_Catalog_Model_Resource
99
  ->join(
100
  array($tableAlias => $this->getMainTable()),
101
  join(' AND ', $conditions),
102
- array('value', 'count' => "COUNT({$tableAlias}.entity_id)"))
103
  ->group("{$tableAlias}.value");
104
 
105
  return $connection->fetchPairs($select);
31
 
32
  $attribute = $filter->getAttributeModel();
33
  $connection = $this->_getReadAdapter();
34
+ switch ($filter->getFilterOptions()->getOperation()) {
35
+ case '':
36
+ $tableAlias = $attribute->getAttributeCode() . '_idx';
37
+ $conditions = array(
38
+ "{$tableAlias}.entity_id = e.entity_id",
39
+ $connection->quoteInto("{$tableAlias}.attribute_id = ?", $attribute->getAttributeId()),
40
+ $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId()),
41
+ "{$tableAlias}.value in (" . implode(',', array_filter(explode('_', $value))) . ")"
42
+ );
43
+ $collection->getSelect()
44
+ ->distinct()
45
+ ->join(
46
+ array($tableAlias => $this->getMainTable()),
47
+ join(' AND ', $conditions),
48
+ array()
49
+ );
50
+ break;
51
+ case 'and':
52
+ foreach (explode('_', $value) as $i => $singleValue) {
53
+ $tableAlias = $attribute->getAttributeCode() . '_idx'.$i;
54
+ $conditions = array(
55
+ "{$tableAlias}.entity_id = e.entity_id",
56
+ $connection->quoteInto("{$tableAlias}.attribute_id = ?", $attribute->getAttributeId()),
57
+ $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId()),
58
+ "{$tableAlias}.value = $singleValue"
59
+ );
60
+ $collection->getSelect()
61
+ ->distinct()
62
+ ->join(
63
+ array($tableAlias => $this->getMainTable()),
64
+ join(' AND ', $conditions),
65
+ array()
66
+ );
67
+ }
68
+ break;
69
+ default: throw new Exception('Not implemented');
70
+ }
71
 
72
  return $this;
73
  }
120
  ->join(
121
  array($tableAlias => $this->getMainTable()),
122
  join(' AND ', $conditions),
123
+ array('value', 'count' => "COUNT(DISTINCT {$tableAlias}.entity_id)"))
124
  ->group("{$tableAlias}.value");
125
 
126
  return $connection->fetchPairs($select);
app/code/local/Mana/Filters/Resource/Filter/Attribute/Collection.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Attribute definition collection for filters
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Resource_Filter_Attribute_Collection extends Mana_Core_Resource_Attribute_Collection {
15
+ public function __construct($resource=null) {
16
+ $this->setEntityType(Mana_Filters_Model_Filter::ENTITY);
17
+ parent::__construct($resource);
18
+ }
19
+ }
app/code/local/Mana/Filters/Resource/Filter/Collection.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* BASED ON SNIPPET: Resources/DB operations with model collections */
9
+ /**
10
+ * This resource model handles DB operations with a collection of models of type Mana_Filters_Model_Filter. All
11
+ * database specific code for operating collection of Mana_Filters_Model_Filter should go here.
12
+ * @author Mana Team
13
+ */
14
+ class Mana_Filters_Resource_Filter_Collection extends Mana_Core_Resource_Eav_Collection
15
+ {
16
+ /**
17
+ * Invoked during resource collection model creation process, this method associates this
18
+ * resource collection model with model class and with resource model class
19
+ */
20
+ protected function _construct()
21
+ {
22
+ $this->_init(strtolower('Mana_Filters/Filter'));
23
+ }
24
+
25
+ protected $_codeFilter;
26
+ public function addCodeFilter($codes) {
27
+ $this->_codeFilter = $codes;
28
+ $this->getSelect()->where('e.code in (?)', $codes);
29
+ return $this;
30
+ }
31
+ protected function _isValidItem($item) {
32
+ if ($item->getCode() == 'category') {
33
+ return true;
34
+ }
35
+ else {
36
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
37
+ $attributes = Mage::getSingleton('mana_filters/filter_default')->getFilterableAttributes($this->getStoreId());
38
+ if ($core->collectionFind($attributes, 'attribute_code', $item->getCode())) {
39
+ return true;
40
+ }
41
+ else {
42
+ /* @var $resource Mana_Filters_Resource_Filter */ $resource = $this->getResource();
43
+ $resource->delete($item);
44
+ return false;
45
+ }
46
+ }
47
+ }
48
+ }
app/code/local/Mana/Filters/Resource/Filter/Collection/Derived.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Virtual collection of filters combining info from multiple places in DB and XML configuration
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Resource_Filter_Collection_Derived extends Mana_Core_Resource_Eav_Collection_Derived {
15
+ protected function _construct()
16
+ {
17
+ $this->_init(strtolower('Mana_Filters/Filter'));
18
+ }
19
+ protected function _addMissingOriginalItems() {
20
+ /* @var $core Mana_Core_Helper_Data */ $core = Mage::helper(strtolower('Mana_Core'));
21
+
22
+ // load system filters
23
+ if ($core->arrayFind($this->_items, 'code', 'category') === false) {
24
+ $this->addItem($this->getNewEmptyItem()
25
+ ->setCode('category')
26
+ ->setStoreId($this->getStoreId())
27
+ ->loadDefaults());
28
+ }
29
+
30
+ // get filterable attributes
31
+ $attributes = Mage::getSingleton('mana_filters/filter_default')->getFilterableAttributes($this->getStoreId());
32
+
33
+ // load attribute filters
34
+ foreach ($attributes as $attribute) {
35
+ if ($core->arrayFind($this->_items, 'code', $attribute->getAttributeCode()) === false) {
36
+ $this->addItem($this->getNewEmptyItem()
37
+ ->setCode($attribute->getAttributeCode())
38
+ ->setAttribute($attribute)
39
+ ->setStoreId($this->getStoreId())
40
+ ->loadDefaults());
41
+ }
42
+ }
43
+
44
+ return $this;
45
+ }
46
+ protected function _renderFilters() {
47
+ parent::_renderFilters();
48
+ if ($this->_codeFilter) {
49
+ $items = array();
50
+ foreach ($this->_items as $key => $item) {
51
+ if (in_array($item->getCode(), $this->_codeFilter)) {
52
+ $items[$key] = $item;
53
+ }
54
+ }
55
+ $this->_items = $items;
56
+ }
57
+ return $this;
58
+ }
59
+
60
+ protected $_codeFilter;
61
+ public function addCodeFilter($codes) {
62
+ $this->_codeFilter = $codes;
63
+ $this->getSelect()->where('e.code in (?)', $codes);
64
+ return $this;
65
+ }
66
+ }
app/code/local/Mana/Filters/Resource/Filter/Decimal.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * Resource type which contains sql code for applying filters and related operations
10
+ * @author Mana Team
11
+ * Injected instead of standard resource catalog/layer_filter_attribute in
12
+ * Mana_Filters_Model_Filter_Price::_getResource().
13
+ */
14
+ class Mana_Filters_Resource_Filter_Decimal extends Mage_Catalog_Model_Resource_Eav_Mysql4_Layer_Filter_Decimal {
15
+ /**
16
+ * Applies one or more price filters to currently viewed product collection
17
+ * @param Mana_Filters_Model_Filter_Price $filter
18
+ * @param array $selections
19
+ * @return Mana_Filters_Resource_Filter_Price
20
+ * This method is cloned from method applyFilterToCollection() in parent class (method body was pasted from parent class
21
+ * and changed as needed. All changes marked with comments
22
+ * Standard method did not give us possibility to filter multiple ranges.
23
+ */
24
+ public function applyFilterToCollectionEx($filter, $selections)
25
+ {
26
+ $collection = $filter->getLayer()->getProductCollection();
27
+ $attribute = $filter->getAttributeModel();
28
+ $connection = $this->_getReadAdapter();
29
+ $tableAlias = $attribute->getAttributeCode() . '_idx';
30
+ $conditions = array(
31
+ "{$tableAlias}.entity_id = e.entity_id",
32
+ $connection->quoteInto("{$tableAlias}.attribute_id = ?", $attribute->getAttributeId()),
33
+ $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId())
34
+ );
35
+
36
+ $collection->getSelect()->join(
37
+ array($tableAlias => $this->getMainTable()),
38
+ join(' AND ', $conditions),
39
+ array()
40
+ );
41
+
42
+ // MANA BEGIN: modify select formation to include multiple price ranges
43
+ $condition = '';
44
+ foreach ($selections as $selection) {
45
+ list($index, $range) = explode(',', $selection);
46
+ $range = $this->getRange($index, $range);
47
+ if ($condition != '') $condition .= ' OR ';
48
+ $condition .= '(('."{$tableAlias}.value" . ' >= '. $range['from'].') '.
49
+ 'AND ('."{$tableAlias}.value" . ($this->_isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
50
+ }
51
+ $collection->getSelect()
52
+ ->distinct()
53
+ ->where($condition);
54
+ // MANA END
55
+
56
+ return $this;
57
+ }
58
+ protected function _isUpperBoundInclusive() {
59
+ return false;
60
+ }
61
+ /**
62
+ * For each option visible to person as a filter choice counts how many products are there given that all the
63
+ * other filters are applied
64
+ * @param Mana_Filters_Model_Filter_Price $filter
65
+ * @param int $range The whole price range is split into several using this range step
66
+ * @return array Each entry in result is int index => int count
67
+ * This method is overridden by copying (method body was pasted from parent class and modified as needed). All
68
+ * changes are marked with comments.
69
+ * @see Mage_Catalog_Model_Resource_Eav_Mysql4_Layer_Filter_Attribute::getCount()
70
+ */
71
+ public function getCount($filter, $range)
72
+ {
73
+ $select = $this->_getSelect($filter);
74
+ $adapter = $this->_getReadAdapter();
75
+
76
+ $countExpr = new Zend_Db_Expr("COUNT(*)");
77
+ $rangeExpr = new Zend_Db_Expr("FLOOR(decimal_index.value / {$range}) + 1");
78
+
79
+ $select->columns(array(
80
+ 'range' => $rangeExpr,
81
+ 'count' => $countExpr
82
+ ));
83
+ $select->group('range');
84
+
85
+ // MANA BEGIN: make sure price filter is not applied
86
+ Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
87
+ // MANA END
88
+
89
+ return $adapter->fetchPairs($select);
90
+ }
91
+ /**
92
+ * Retrieve maximal price for attribute
93
+ *
94
+ * @param Mage_Catalog_Model_Layer_Filter_Price $filter
95
+ * @return float
96
+ */
97
+ public function getMinMax($filter)
98
+ {
99
+ $select = $this->_getSelect($filter);
100
+ $connection = $this->_getReadAdapter();
101
+
102
+ $table = 'decimal_index';
103
+
104
+ $select->columns(array(
105
+ 'min_value' => new Zend_Db_Expr('MIN(decimal_index.value)'),
106
+ 'max_value' => new Zend_Db_Expr('MAX(decimal_index.value)'),
107
+ ));
108
+ Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
109
+ $from = $select->getPart(Zend_Db_Select::FROM);
110
+ foreach ($from as $key => $options) {
111
+ if ($key == 'cat_index') {
112
+ /* @var $layer Mage_Catalog_Model_Layer */ $layer = Mage::getSingleton('catalog/layer');
113
+ $needle = "cat_index.category_id='";
114
+ $startPos = strpos($options['joinCondition'], $needle);
115
+ if ($startPos === false) throw new Exception('Not implemented');
116
+ $endPos = strpos($options['joinCondition'], "'", $startPos + strlen($needle));
117
+ $from[$key]['joinCondition'] =
118
+ substr($options['joinCondition'], 0, $startPos + strlen($needle)).
119
+ $layer->getCurrentCategory()->getId().
120
+ substr($options['joinCondition'], $endPos);
121
+ }
122
+ elseif (strrpos($key, '_idx') === strlen($key) - strlen('_idx')) {
123
+ unset($from[$key]);
124
+ }
125
+ }
126
+ $select->setPart(Zend_Db_Select::FROM, $from);
127
+
128
+ $result = $connection->fetchRow($select);
129
+ return array($result['min_value'], $result['max_value']);
130
+ }
131
+
132
+ public function getRange($index, $range) {
133
+ return array('from' => $range * ($index - 1), 'to' => $range * $index);
134
+ }
135
+ }
app/code/local/Mana/Filters/Resource/Filter/Price.php CHANGED
@@ -31,24 +31,34 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
31
 
32
  $table = $this->_getIndexTableAlias();
33
  $additional = join('', $response->getAdditionalCalculations());
 
34
  $rate = $filter->getCurrencyRate();
35
- $priceExpr = new Zend_Db_Expr("(({$table}.min_price {$additional}) * {$rate})");
 
 
 
 
 
 
36
 
37
  // MANA BEGIN: modify select formation to include multiple price ranges
38
- /* @var $ext Mana_Filters_Helper_Extended */ $ext = Mage::helper(strtolower('Mana_Filters/Extended'));
39
  $condition = '';
40
  foreach ($selections as $selection) {
41
  list($index, $range) = explode(',', $selection);
42
- $range = $ext->getPriceRange($index, $range);
43
  if ($condition != '') $condition .= ' OR ';
44
  $condition .= '(('.$priceExpr . ' >= '. $range['from'].') '.
45
- 'AND ('.$priceExpr . ' < '. $range['to'].'))';
46
  }
47
  $select
 
48
  ->where($condition);
49
  // MANA END
50
  return $this;
51
  }
 
 
 
52
  /**
53
  * For each option visible to person as a filter choice counts how many products are there given that all the
54
  * other filters are applied
@@ -59,31 +69,58 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
59
  * changes are marked with comments.
60
  * @see Mage_Catalog_Model_Resource_Eav_Mysql4_Layer_Filter_Attribute::getCount()
61
  */
62
- public function getCount($filter, $range)
63
- {
64
- $select = $this->_getSelect($filter);
65
- $connection = $this->_getReadAdapter();
66
- $response = $this->_dispatchPreparePriceEvent($filter, $select);
67
- $table = $this->_getIndexTableAlias();
68
 
69
- $additional = join('', $response->getAdditionalCalculations());
70
- $rate = $filter->getCurrencyRate();
71
- $countExpr = new Zend_Db_Expr('COUNT(*)');
72
- $rangeExpr = new Zend_Db_Expr("FLOOR((({$table}.min_price {$additional}) * {$rate}) / {$range}) + 1");
73
-
74
- $select->columns(array(
75
- 'range' => $rangeExpr,
76
- 'count' => $countExpr
77
- ));
78
-
79
- // MANA BEGIN: make sure price filter is not applied
80
- $select->reset(Zend_Db_Select::WHERE);
81
- // MANA END
82
-
83
- $select->where("{$table}.min_price > 0");
84
- $select->group('range');
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
- return $connection->fetchPairs($select);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
  /**
89
  * Retrieve maximal price for attribute
@@ -97,13 +134,18 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
97
  $connection = $this->_getReadAdapter();
98
  $response = $this->_dispatchPreparePriceEvent($filter, $select);
99
 
100
- $table = $this->_getIndexTableAlias();
 
 
 
 
 
101
 
102
  $additional = join('', $response->getAdditionalCalculations());
103
- $maxPriceExpr = new Zend_Db_Expr("MAX({$table}.min_price {$additional})");
104
 
105
  // MANA BEGIN: make sure no filter is applied
106
- $select->reset(Zend_Db_Select::WHERE);
107
  $from = $select->getPart(Zend_Db_Select::FROM);
108
  foreach ($from as $key => $options) {
109
  if ($key == 'cat_index') {
@@ -123,9 +165,43 @@ class Mana_Filters_Resource_Filter_Price extends Mage_Catalog_Model_Resource_Eav
123
  }
124
  $select->setPart(Zend_Db_Select::FROM, $from);
125
  // MANA END
126
- $select->columns(array($maxPriceExpr));
127
 
128
- return $connection->fetchOne($select) * $filter->getCurrencyRate();
 
 
 
 
 
129
  }
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
31
 
32
  $table = $this->_getIndexTableAlias();
33
  $additional = join('', $response->getAdditionalCalculations());
34
+ $fix = $this->_getConfigurablePriceFix();
35
  $rate = $filter->getCurrencyRate();
36
+ $precision = 2;//$filter->getDecimalDigits();
37
+ if ($this->_isUpperBoundInclusive()) {
38
+ $priceExpr = new Zend_Db_Expr("ROUND(({$table}.min_price {$additional} {$fix}) * {$rate}, $precision)");
39
+ }
40
+ else {
41
+ $priceExpr = new Zend_Db_Expr("({$table}.min_price {$additional} {$fix}) * {$rate}");
42
+ }
43
 
44
  // MANA BEGIN: modify select formation to include multiple price ranges
 
45
  $condition = '';
46
  foreach ($selections as $selection) {
47
  list($index, $range) = explode(',', $selection);
48
+ $range = $this->getPriceRange($index, $range);
49
  if ($condition != '') $condition .= ' OR ';
50
  $condition .= '(('.$priceExpr . ' >= '. $range['from'].') '.
51
+ 'AND ('.$priceExpr . ($this->_isUpperBoundInclusive() ? ' <= ' : ' < '). $range['to'].'))';
52
  }
53
  $select
54
+ ->distinct()
55
  ->where($condition);
56
  // MANA END
57
  return $this;
58
  }
59
+ protected function _isUpperBoundInclusive() {
60
+ return false;
61
+ }
62
  /**
63
  * For each option visible to person as a filter choice counts how many products are there given that all the
64
  * other filters are applied
69
  * changes are marked with comments.
70
  * @see Mage_Catalog_Model_Resource_Eav_Mysql4_Layer_Filter_Attribute::getCount()
71
  */
72
+ public function getCount($filter, $range) {
73
+ if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
74
+ $table = Mage_Catalog_Model_Resource_Product_Collection::MAIN_TABLE_ALIAS;
75
+ $select = $this->_getSelect($filter);
76
+ $priceExpression = $this->_getFullPriceExpression($filter, $select);
 
77
 
78
+ $range = floatval($range);
79
+ if ($range == 0) {
80
+ $range = 1;
81
+ }
82
+ $fix = $this->_getConfigurablePriceFix();
83
+ $countExpr = new Zend_Db_Expr('COUNT(*)');
84
+ $rangeExpr = new Zend_Db_Expr("FLOOR(({$priceExpression} {$fix}) / {$range}) + 1");
85
+
86
+ $select->columns(array(
87
+ 'range' => $rangeExpr,
88
+ 'count' => $countExpr
89
+ ));
90
+ $select->group($rangeExpr)->order("$rangeExpr ASC");
91
+
92
+ Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
93
+ $select->where("{$table}.min_price > 0");
94
+
95
+ return $this->_getReadAdapter()->fetchPairs($select);
96
+ }
97
+ else {
98
+ $select = $this->_getSelect($filter);
99
+ $connection = $this->_getReadAdapter();
100
+ $response = $this->_dispatchPreparePriceEvent($filter, $select);
101
+ $table = $this->_getIndexTableAlias();
102
+ $additional = join('', $response->getAdditionalCalculations());
103
+ $fix = $this->_getConfigurablePriceFix();
104
+ $rate = $filter->getCurrencyRate();
105
+ $countExpr = new Zend_Db_Expr('COUNT(*)');
106
+ $rangeExpr = new Zend_Db_Expr("FLOOR((({$table}.min_price {$additional} {$fix}) * {$rate}) / {$range}) + 1");
107
 
108
+ $select->columns(array(
109
+ 'range' => $rangeExpr,
110
+ 'count' => $countExpr
111
+ ));
112
+
113
+ // MANA BEGIN: make sure price filter is not applied
114
+ Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
115
+ // MANA END
116
+
117
+ $select->where("{$table}.min_price > 0");
118
+ $select->group('range');
119
+
120
+ $result = $connection->fetchPairs($select);
121
+ }
122
+
123
+ return $result;
124
  }
125
  /**
126
  * Retrieve maximal price for attribute
134
  $connection = $this->_getReadAdapter();
135
  $response = $this->_dispatchPreparePriceEvent($filter, $select);
136
 
137
+ if (Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) {
138
+ $table = Mage_Catalog_Model_Resource_Product_Collection::MAIN_TABLE_ALIAS;
139
+ }
140
+ else {
141
+ $table = $this->_getIndexTableAlias();
142
+ }
143
 
144
  $additional = join('', $response->getAdditionalCalculations());
145
+ $maxPriceExpr = new Zend_Db_Expr("MAX({$table}.min_price {$additional}) AS m_max_price");
146
 
147
  // MANA BEGIN: make sure no filter is applied
148
+ Mage::helper('mana_filters')->resetProductCollectionWhereClause($select);
149
  $from = $select->getPart(Zend_Db_Select::FROM);
150
  foreach ($from as $key => $options) {
151
  if ($key == 'cat_index') {
165
  }
166
  $select->setPart(Zend_Db_Select::FROM, $from);
167
  // MANA END
168
+ $select->columns(array($maxPriceExpr))->order('m_max_price DESC');
169
 
170
+ $result = $connection->fetchOne($select) * $filter->getCurrencyRate();
171
+ // Mage::log('MAX select: ' . ((string)$select), Zend_Log::DEBUG, 'price.log');
172
+ // Mage::log("MAX result: $result", Zend_Log::DEBUG, 'price.log');
173
+ // Mage::log('LIST select: '. (string)$filter->getLayer()->getProductCollection()->getSelect(), Zend_Log::DEBUG, 'price.log');
174
+ // $this->getCount($filter, 1);
175
+ return $result;
176
  }
177
 
178
+ public function getPriceRange($index, $range) {
179
+ return array('from' => $range * ($index - 1), 'to' => $range * $index);
180
+ }
181
+
182
+ protected function _getConfigurablePriceFix() {
183
+ if (!Mage::getStoreConfigFlag('mana_filters/general/adjust_configurable_price')) {
184
+ return '';
185
+ }
186
+ /* @var $db Mage_Core_Model_Resource */ $db = Mage::getSingleton('core/resource');
187
+ $request = Mage::app()->getRequest();
188
+ $subselect = '';
189
+
190
+ $values = array();
191
+ foreach (Mage::helper('mana_filters')->getFilterOptionsCollection() as $filter) {
192
+ if ($filter->getType() == 'attribute' && ($param = $request->getParam($filter->getCode()))) {
193
+ $values = array_merge($values, explode('_', $param));
194
+ }
195
+ }
196
+ if (count($values) > 0) {
197
+ $values = implode(',', $values);
198
+ $subselect = "SELECT SUM(super_price.pricing_value) ".
199
+ "FROM {$db->getTableName('catalog/product_super_attribute')} AS super ".
200
+ "INNER JOIN {$db->getTableName('catalog/product_super_attribute_pricing')} AS super_price ".
201
+ "ON super.product_super_attribute_id = super_price.product_super_attribute_id AND ".
202
+ "super_price.is_percent = 0 AND super_price.value_index IN ($values) ".
203
+ "WHERE super.product_id = e.entity_id";
204
+ }
205
+ return $subselect ? " + COALESCE(($subselect), 0)" : '';
206
+ }
207
  }
app/code/local/Mana/Filters/Resource/Filter2.php ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Resources/Single model DB operations */
10
+ /**
11
+ * This resource model handles DB operations with a single model of type Mana_Filters_Model_Filter2. All
12
+ * database specific code for Mana_Filters_Model_Filter2 should go here.
13
+ * @author Mana Team
14
+ */
15
+ class Mana_Filters_Resource_Filter2 extends Mana_Db_Resource_Object {
16
+ /**
17
+ * Invoked during resource model creation process, this method associates this resource model with model class
18
+ * and with DB table name
19
+ */
20
+ protected function _construct() {
21
+ $this->_init(strtolower('Mana_Filters/Filter2'), 'id');
22
+ $this->_isPkAutoIncrement = false;
23
+ }
24
+ protected function _getReplicationSources() {
25
+ return array('eav/attribute');
26
+ }
27
+ /**
28
+ * Enter description here ...
29
+ * @param Mana_Db_Model_Replication_Target $target
30
+ */
31
+ protected function _prepareReplicationUpdateSelects($target, $options) {
32
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
33
+ $select
34
+ ->from(array('eav_attribute' => Mage::getSingleton('core/resource')->getTableName('eav/attribute')), null)
35
+ ->joinInner(array('eav_attribute_additional' => Mage::getSingleton('core/resource')->getTableName('catalog/eav_attribute')),
36
+ 'eav_attribute.attribute_id = eav_attribute_additional.attribute_id', null)
37
+ ->joinInner(array('eav_entity_type' => Mage::getSingleton('core/resource')->getTableName('eav/entity_type')),
38
+ 'eav_attribute.entity_type_id = eav_entity_type.entity_type_id', null)
39
+ ->joinInner(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
40
+ 'target.code = eav_attribute.attribute_code',
41
+ array('target.id AS id', 'target.code AS code', 'target.type AS type'))
42
+ ->distinct()
43
+ ->where('eav_entity_type.entity_type_code = ?', 'catalog_product')
44
+ ->where('eav_attribute_additional.is_filterable <> 0')
45
+ ->columns(array(
46
+ 'target.default_mask0 AS default_mask0',
47
+ 'eav_attribute_additional.is_filterable AS is_enabled',
48
+ 'eav_attribute.frontend_label AS name',
49
+ 'eav_attribute_additional.is_filterable_in_search AS is_enabled_in_search',
50
+ 'eav_attribute_additional.position AS position',
51
+ ));
52
+
53
+ if ($options['trackKeys']) {
54
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
55
+ $select->where('eav_attribute.attribute_id IN (?)', $keys);
56
+ $target->setIsKeyFilterApplied(true);
57
+ }
58
+ if (($keys = $options['targets'][$this->getEntityName()]->getSavedKeys()) && count($keys)) {
59
+ $select->where('target.id IN (?)', $keys);
60
+ $target->setIsKeyFilterApplied(true);
61
+ }
62
+ }
63
+ $target->setSelect('main', $select);
64
+
65
+ $select = $options['db']->select()
66
+ ->from(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
67
+ array('target.id AS id', 'target.code AS code', "target.type AS type"))
68
+ ->where("target.code = ?", 'category')
69
+ ->columns(array(
70
+ 'target.default_mask0 AS default_mask0',
71
+ '(1) AS is_enabled',
72
+ "('Category') AS name",
73
+ '(1) AS is_enabled_in_search',
74
+ '(-1) AS position',
75
+ ));
76
+ if ($options['trackKeys']) {
77
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
78
+ $select->where('(1 <> 1)');
79
+ $target->setIsKeyFilterApplied(true);
80
+ }
81
+ if (($keys = $options['targets'][$this->getEntityName()]->getSavedKeys()) && count($keys)) {
82
+ $select->where('target.id IN (?)', $keys);
83
+ $target->setIsKeyFilterApplied(true);
84
+ }
85
+ }
86
+ $target->setSelect('category', $select);
87
+
88
+ }
89
+ /**
90
+ * Enter description here ...
91
+ * @param Mana_Db_Model_Object $object
92
+ * @param array $values
93
+ * @param array $options
94
+ */
95
+ protected function _processReplicationUpdate($object, $values, $options) {
96
+ $object
97
+ ->setId($values['id'])
98
+ ->setCode($values['code'])
99
+ ->setType($values['type'])
100
+ ->setData('_m_prevent_replication', true);
101
+
102
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 0)) {
103
+ $object->setIsEnabled($values['is_enabled']);
104
+ }
105
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 1)) {
106
+ $object->setDisplay(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/'.$object->getType()));
107
+ }
108
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 2)) {
109
+ $object->setName($values['name']);
110
+ }
111
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 4)) {
112
+ $object->setIsEnabledInSearch($values['is_enabled_in_search']);
113
+ }
114
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 5)) {
115
+ $object->setPosition($values['position']);
116
+ }
117
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 25)) {
118
+ $object->setSortMethod(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/sort_method'));
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Enter description here ...
124
+ * @param Mana_Db_Model_Replication_Target $target
125
+ */
126
+ protected function _prepareReplicationInsertSelects($target, $options) {
127
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
128
+ $select
129
+ ->from(array('eav_attribute' => Mage::getSingleton('core/resource')->getTableName('eav/attribute')), null)
130
+ ->joinInner(array('eav_attribute_additional' => Mage::getSingleton('core/resource')->getTableName('catalog/eav_attribute')),
131
+ 'eav_attribute.attribute_id = eav_attribute_additional.attribute_id', null)
132
+ ->joinInner(array('eav_entity_type' => Mage::getSingleton('core/resource')->getTableName('eav/entity_type')),
133
+ 'eav_attribute.entity_type_id = eav_entity_type.entity_type_id', null)
134
+ ->joinLeft(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
135
+ 'target.code = eav_attribute.attribute_code', null)
136
+ ->distinct()
137
+ ->where('eav_entity_type.entity_type_code = ?', 'catalog_product')
138
+ ->where('eav_attribute_additional.is_filterable <> 0')
139
+ ->where('target.id IS NULL')
140
+ ->columns(array(
141
+ 'eav_attribute.attribute_code AS code',
142
+ "IF(eav_attribute.attribute_code = 'category', 'category', IF(eav_attribute.attribute_code = 'price', 'price', IF(eav_attribute.backend_type = 'decimal', 'decimal', 'attribute'))) AS type",
143
+ 'eav_attribute_additional.is_filterable AS is_enabled',
144
+ 'eav_attribute.frontend_label AS name',
145
+ 'eav_attribute_additional.is_filterable_in_search AS is_enabled_in_search',
146
+ 'eav_attribute_additional.position AS position',
147
+ ));
148
+
149
+ if ($options['trackKeys']) {
150
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
151
+ $select->where('eav_attribute.attribute_id IN (?)', $keys);
152
+ $target->setIsKeyFilterApplied(true);
153
+ }
154
+ }
155
+ $target->setSelect('main', $select);
156
+
157
+ $select = $options['db']->select()
158
+ ->from(array('source' => $options['db']->select()->from(array(), "('category') AS code")), null)
159
+ ->joinLeft(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
160
+ "target.code = 'category'", null)
161
+ ->where('target.id IS NULL')
162
+ ->columns(array(
163
+ "('category') AS code",
164
+ "('category') AS type",
165
+ '(1) AS is_enabled',
166
+ "('Category') AS name",
167
+ '(1) AS is_enabled_in_search',
168
+ '(-1) AS position',
169
+ ));
170
+ if ($options['trackKeys']) {
171
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
172
+ $select->where('(1 <> 1)');
173
+ $target->setIsKeyFilterApplied(true);
174
+ }
175
+ }
176
+ $target->setSelect('category', $select);
177
+
178
+ }
179
+ /**
180
+ * Enter description here ...
181
+ * @param Mana_Db_Model_Object $object
182
+ * @param array $values
183
+ * @param array $options
184
+ */
185
+ protected function _processReplicationInsert($object, $values, $options) {
186
+ $object
187
+ ->setCode($values['code'])
188
+ ->setType($values['type'])
189
+ ->setData('_m_prevent_replication', true);
190
+
191
+ $object->setIsEnabled($values['is_enabled']);
192
+ $object->setDisplay(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/'.$object->getType()));
193
+ $object->setName($values['name']);
194
+ $object->setIsEnabledInSearch($values['is_enabled_in_search']);
195
+ $object->setPosition($values['position']);
196
+ $object->setSortMethod(Mage::helper('mana_db')->getLatestConfig('mana_filters/display/sort_method'));
197
+ }
198
+ /**
199
+ * Enter description here ...
200
+ * @param Mana_Db_Model_Replication_Target $target
201
+ */
202
+ protected function _prepareReplicationDeleteSelects($target, $options) {
203
+ if ($options['trackKeys']) {
204
+ if (($keys = $options['targets']['eav/attribute']->getDeletedKeys()) && count($keys)) {
205
+ $attributeJoin = '';
206
+ //$attributeJoin = ' AND '.$options['db']->quoteInto('eav_attribute.attribute_id IN (?)', $keys);
207
+ $target->setIsKeyFilterApplied(true);
208
+ }
209
+ else {
210
+ $attributeJoin = '';
211
+ }
212
+ }
213
+ else {
214
+ $attributeJoin = '';
215
+ }
216
+
217
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
218
+ $select
219
+ ->from(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())), 'target.id AS id')
220
+ ->joinLeft(array('eav_attribute' => Mage::getSingleton('core/resource')->getTableName('eav/attribute')),
221
+ 'target.code = eav_attribute.attribute_code'.$attributeJoin, null)
222
+ ->joinLeft(array('eav_attribute_additional' => Mage::getSingleton('core/resource')->getTableName('catalog/eav_attribute')),
223
+ 'eav_attribute.attribute_id = eav_attribute_additional.attribute_id', null)
224
+ ->distinct()
225
+ ->where('(eav_attribute.attribute_id IS NULL) OR (eav_attribute_additional.is_filterable = 0)')
226
+ ->where("target.code <> 'category'");
227
+ $target->setSelect('main', $select);
228
+ }
229
+ /**
230
+ * Enter description here ...
231
+ * @param array $values
232
+ * @param array $options
233
+ */
234
+ protected function _processReplicationDelete($values, $options) {
235
+ if (count($values)) {
236
+ $values = implode(',', $values);
237
+ $table = Mage::getSingleton('core/resource')->getTableName($this->getEntityName());
238
+ $options['db']->query("DELETE FROM {$table} WHERE id IN ($values)");
239
+ }
240
+ }
241
+
242
+ protected function _addEditedData($object, $fields, $useDefault) {
243
+ Mage::helper('mana_db')->updateDefaultableField($object, 'is_enabled', 0, $fields, $useDefault);
244
+ Mage::helper('mana_db')->updateDefaultableField($object, 'display', 1, $fields, $useDefault);
245
+ Mage::helper('mana_db')->updateDefaultableField($object, 'name', 2, $fields, $useDefault);
246
+ Mage::helper('mana_db')->updateDefaultableField($object, 'is_enabled_in_search', 4, $fields, $useDefault);
247
+ Mage::helper('mana_db')->updateDefaultableField($object, 'position', 5, $fields, $useDefault);
248
+ Mage::helper('mana_db')->updateDefaultableField($object, 'sort_method', 25, $fields, $useDefault);
249
+ Mage::helper('mana_db')->updateDefaultableField($object, 'operation', 27, $fields, $useDefault);
250
+ }
251
+ protected function _afterSave(Mage_Core_Model_Abstract $object) {
252
+ if ($edit = $object->getValueData()) {
253
+ foreach ($edit['saved'] as $id => $editId) {
254
+ if ($id > 0) {
255
+ $editModel = Mage::helper('mana_admin')->loadModel('mana_filters/filter2_value', $editId);
256
+ $data = $editModel->getData();
257
+ unset($data['id']);
258
+ unset($data['edit_status']);
259
+ unset($data['edit_session_id']);
260
+ $model = Mage::helper('mana_admin')->loadModel('mana_filters/filter2_value', $id)->addData($data);
261
+ // validation code here
262
+ $model->save();
263
+ $editModel->delete();
264
+ }
265
+ else {
266
+ // inserts
267
+ throw new Exception('Not implemented!');
268
+ }
269
+ }
270
+ foreach ($edit['deleted'] as $id) {
271
+ // deletes
272
+ throw new Exception('Not implemented!');
273
+ }
274
+ }
275
+ $object->unsValueData();
276
+ $object->setHadValueData(true);
277
+ return parent::_afterSave($object);
278
+ }
279
+ }
app/code/local/Mana/Filters/Resource/Filter2/Collection.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Resources/DB operations with model collections */
10
+ /**
11
+ * This resource model handles DB operations with a collection of models of type Mana_Filters_Model_Filter2. All
12
+ * database specific code for operating collection of Mana_Filters_Model_Filter2 should go here.
13
+ * @author Mana Team
14
+ */
15
+ class Mana_Filters_Resource_Filter2_Collection extends Mana_Db_Resource_Object_Collection
16
+ {
17
+ /**
18
+ * Invoked during resource collection model creation process, this method associates this
19
+ * resource collection model with model class and with resource model class
20
+ */
21
+ protected function _construct()
22
+ {
23
+ $this->_init(strtolower('Mana_Filters/Filter2'));
24
+ }
25
+
26
+ public function addCodeFilter($codes) {
27
+ $this->addFieldToFilter('code', array('in' => $codes));
28
+ return $this;
29
+ }
30
+ }
app/code/local/Mana/Filters/Resource/Filter2/Store.php ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Resources/Single model DB operations */
10
+ /**
11
+ * This resource model handles DB operations with a single model of type Mana_Filters_Model_Filter2_Store. All
12
+ * database specific code for Mana_Filters_Model_Filter2_Store should go here.
13
+ * @author Mana Team
14
+ */
15
+ class Mana_Filters_Resource_Filter2_Store extends Mana_Filters_Resource_Filter2 {
16
+ /**
17
+ * Invoked during resource model creation process, this method associates this resource model with model class
18
+ * and with DB table name
19
+ */
20
+ protected function _construct() {
21
+ $this->_init(strtolower('Mana_Filters/Filter2_Store'), 'id');
22
+ $this->_isPkAutoIncrement = false;
23
+ }
24
+ protected function _getReplicationSources() {
25
+ return array('mana_filters/filter2', 'core/store', 'eav/attribute');
26
+ }
27
+ /**
28
+ * Enter description here ...
29
+ * @param Mana_Db_Model_Replication_Target $target
30
+ */
31
+ protected function _prepareReplicationUpdateSelects($target, $options) {
32
+ $globalEntityName = Mage::helper('mana_db')->getGlobalEntityName($this->getEntityName());
33
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
34
+ $select
35
+ ->from(array('global' => Mage::getSingleton('core/resource')->getTableName($globalEntityName)), null)
36
+ ->joinInner(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
37
+ 'target.global_id = global.id',
38
+ array('target.id AS id', 'target.global_id AS global_id', 'target.store_id AS store_id'))
39
+ ->joinLeft(array('eav_attribute' => Mage::getSingleton('core/resource')->getTableName('eav/attribute')),
40
+ 'global.code = eav_attribute.attribute_code', null)
41
+ ->joinLeft(array('eav_attribute_label' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_label')),
42
+ 'eav_attribute.attribute_id = eav_attribute_label.attribute_id AND target.store_id = eav_attribute_label.store_id',
43
+ null)
44
+ ->distinct()
45
+ ->columns(array(
46
+ 'target.default_mask0 AS default_mask0',
47
+ 'global.is_enabled AS is_enabled',
48
+ 'global.display AS display',
49
+ 'COALESCE(eav_attribute_label.value, global.name) AS name',
50
+ 'global.is_enabled_in_search AS is_enabled_in_search',
51
+ 'global.position AS position',
52
+ 'global.sort_method AS sort_method',
53
+ 'global.operation AS operation',
54
+ ));
55
+ if ($options['trackKeys']) {
56
+ if (($keys = $options['targets'][$globalEntityName]->getSavedKeys()) && count($keys)) {
57
+ $select->where('global.id IN (?)', $keys);
58
+ $target->setIsKeyFilterApplied(true);
59
+ }
60
+ if (($keys = $options['targets'][$this->getEntityName()]->getSavedKeys()) && count($keys)) {
61
+ $select->where('target.id IN (?)', $keys);
62
+ $target->setIsKeyFilterApplied(true);
63
+ }
64
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
65
+ $select->where('eav_attribute.attribute_id IN (?)', $keys);
66
+ $target->setIsKeyFilterApplied(true);
67
+ }
68
+ }
69
+ $target->setSelect('main', $select);
70
+ }
71
+ /**
72
+ * Enter description here ...
73
+ * @param Mana_Db_Model_Object $object
74
+ * @param array $values
75
+ * @param array $options
76
+ */
77
+ protected function _processReplicationUpdate($object, $values, $options) {
78
+ $object
79
+ ->setId($values['id'])
80
+ ->setGlobalId($values['global_id'])
81
+ ->setStoreId($values['store_id'])
82
+ ->setData('_m_prevent_replication', true);
83
+
84
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 0)) {
85
+ $object->setIsEnabled($values['is_enabled']);
86
+ }
87
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 1)) {
88
+ $object->setDisplay($values['display']);
89
+ }
90
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 2)) {
91
+ $object->setName($values['name']);
92
+ }
93
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 4)) {
94
+ $object->setIsEnabledInSearch($values['is_enabled_in_search']);
95
+ }
96
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 5)) {
97
+ $object->setPosition($values['position']);
98
+ }
99
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 25)) {
100
+ $object->setSortMethod($values['sort_method']);
101
+ }
102
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 27)) {
103
+ $object->setOperation($values['operation']);
104
+ }
105
+ }
106
+ /**
107
+ * Enter description here ...
108
+ * @param Mana_Db_Model_Replication_Target $target
109
+ */
110
+ protected function _prepareReplicationInsertSelects($target, $options) {
111
+ $globalEntityName = Mage::helper('mana_db')->getGlobalEntityName($this->getEntityName());
112
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
113
+ $select
114
+ ->from(array('global' => Mage::getSingleton('core/resource')->getTableName($globalEntityName)), 'global.id AS global_id')
115
+ ->from(array('core_store' => Mage::getSingleton('core/resource')->getTableName('core_store')), 'core_store.store_id AS store_id')
116
+ ->joinLeft(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
117
+ 'target.global_id = global.id AND target.store_id = core_store.store_id', null)
118
+ ->joinLeft(array('eav_attribute' => Mage::getSingleton('core/resource')->getTableName('eav/attribute')),
119
+ 'global.code = eav_attribute.attribute_code', null)
120
+ ->joinLeft(array('eav_attribute_label' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_label')),
121
+ 'eav_attribute.attribute_id = eav_attribute_label.attribute_id AND target.store_id = eav_attribute_label.store_id',
122
+ null)
123
+ ->distinct()
124
+ ->where('core_store.store_id <> 0')
125
+ ->where('target.id IS NULL')
126
+ ->columns(array(
127
+ 'global.is_enabled AS is_enabled',
128
+ 'global.display AS display',
129
+ 'COALESCE(eav_attribute_label.value, global.name) AS name',
130
+ 'global.is_enabled_in_search AS is_enabled_in_search',
131
+ 'global.position AS position',
132
+ 'global.sort_method AS sort_method',
133
+ 'global.operation AS operation',
134
+ ));
135
+ if ($options['trackKeys']) {
136
+ if (($keys = $options['targets'][$globalEntityName]->getSavedKeys()) && count($keys)) {
137
+ $select->where('global.id IN (?)', $keys);
138
+ $target->setIsKeyFilterApplied(true);
139
+ }
140
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
141
+ $select->where('eav_attribute.attribute_id IN (?)', $keys);
142
+ $target->setIsKeyFilterApplied(true);
143
+ }
144
+ }
145
+ $target->setSelect('main', $select);
146
+ }
147
+ /**
148
+ * Enter description here ...
149
+ * @param Mana_Db_Model_Object $object
150
+ * @param array $values
151
+ * @param array $options
152
+ */
153
+ protected function _processReplicationInsert($object, $values, $options) {
154
+ $object
155
+ ->setGlobalId($values['global_id'])
156
+ ->setStoreId($values['store_id'])
157
+ ->setData('_m_prevent_replication', true);
158
+
159
+ $object->setIsEnabled($values['is_enabled']);
160
+ $object->setDisplay($values['display']);
161
+ $object->setName($values['name']);
162
+ $object->setIsEnabledInSearch($values['is_enabled_in_search']);
163
+ $object->setPosition($values['position']);
164
+ $object->setSortMethod($values['sort_method']);
165
+ $object->setOperation($values['operation']);
166
+ }
167
+ /**
168
+ * Enter description here ...
169
+ * @param Mana_Db_Model_Replication_Target $target
170
+ */
171
+ protected function _prepareReplicationDeleteSelects($target, $options) {
172
+ }
173
+ /**
174
+ * Enter description here ...
175
+ * @param array $values
176
+ * @param array $options
177
+ */
178
+ protected function _processReplicationDelete($values, $options) {
179
+ }
180
+ /**
181
+ * Enter description here ...
182
+ * @param Mana_Db_Model_Virtual_Result $result
183
+ * @param Varien_Db_Select $select
184
+ * @param array $columns
185
+ */
186
+ protected function _addVirtualColumns($result, $select, $columns = null) {
187
+ $globalEntityName = Mage::helper('mana_db')->getGlobalEntityName($this->getEntityName());
188
+ if (!$columns || in_array('code', $columns)) {
189
+ Mage::helper('mana_db')->joinLeft($select,
190
+ 'global', Mage::getSingleton('core/resource')->getTableName($globalEntityName),
191
+ $this->getMainTable().'.global_id = global.id');
192
+ $select->columns("global.code AS code");
193
+ $result->addColumn('code');
194
+ }
195
+ if (!$columns || in_array('type', $columns)) {
196
+ Mage::helper('mana_db')->joinLeft($select,
197
+ 'global', Mage::getSingleton('core/resource')->getTableName($globalEntityName),
198
+ $this->getMainTable().'.global_id = global.id');
199
+ $select->columns("global.type AS type");
200
+ $result->addColumn('type');
201
+ }
202
+ }
203
+ }
app/code/local/Mana/Filters/Resource/Filter2/Store/Collection.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Resources/DB operations with model collections */
10
+ /**
11
+ * This resource model handles DB operations with a collection of models of type Mana_Filters_Model_Filter2_Store. All
12
+ * database specific code for operating collection of Mana_Filters_Model_Filter2_Store should go here.
13
+ * @author Mana Team
14
+ */
15
+ class Mana_Filters_Resource_Filter2_Store_Collection extends Mana_Filters_Resource_Filter2_Collection {
16
+ /**
17
+ * Invoked during resource collection model creation process, this method associates this
18
+ * resource collection model with model class and with resource model class
19
+ */
20
+ protected function _construct()
21
+ {
22
+ $this->_init(strtolower('Mana_Filters/Filter2_Store'));
23
+ }
24
+ /**
25
+ * Enter description here ...
26
+ * @param Mana_Db_Model_Virtual_Result $result
27
+ * @param Varien_Db_Select $select
28
+ * @param array $columns
29
+ */
30
+ protected function _addVirtualColumns($result, $select, $columns = null) {
31
+ $globalEntityName = Mage::helper('mana_db')->getGlobalEntityName($this->getEntityName());
32
+ if (!$columns || in_array('code', $columns)) {
33
+ Mage::helper('mana_db')->joinLeft($select,
34
+ 'global', Mage::getSingleton('core/resource')->getTableName($globalEntityName),
35
+ 'main_table.global_id = global.id');
36
+ $select->columns("global.code AS code");
37
+ $result->addColumn('code');
38
+ }
39
+ if (!$columns || in_array('type', $columns)) {
40
+ Mage::helper('mana_db')->joinLeft($select,
41
+ 'global', Mage::getSingleton('core/resource')->getTableName($globalEntityName),
42
+ 'main_table.global_id = global.id');
43
+ $select->columns("global.type AS type");
44
+ $result->addColumn('type');
45
+ }
46
+ }
47
+ public function addGlobalFields($fields) {
48
+ $select = $this->_select;
49
+ $globalEntityName = Mage::helper('mana_db')->getGlobalEntityName($this->getEntityName());
50
+ Mage::helper('mana_db')->joinLeft($select,
51
+ 'global', Mage::getSingleton('core/resource')->getTableName($globalEntityName),
52
+ 'main_table.global_id = global.id');
53
+ $fields = array_merge(array('default_mask0'), $fields);
54
+ foreach ($fields as $field) {
55
+ $select->columns("global.$field AS global_$field");
56
+ }
57
+ return $this;
58
+ }
59
+ }
app/code/local/Mana/Filters/Resource/Filter2/Value.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Resources/Single model DB operations */
10
+ /**
11
+ * This resource model handles DB operations with a single model of type Mana_Filters_Model_Filter2_Value. All
12
+ * database specific code for Mana_Filters_Model_Filter2_Value should go here.
13
+ * @author Mana Team
14
+ */
15
+ class Mana_Filters_Resource_Filter2_Value extends Mana_Db_Resource_Object {
16
+ /**
17
+ * Invoked during resource model creation process, this method associates this resource model with model class
18
+ * and with DB table name
19
+ */
20
+ protected function _construct() {
21
+ $this->_init(strtolower('Mana_Filters/Filter2_Value'), 'id');
22
+ $this->_isPkAutoIncrement = false;
23
+ }
24
+ protected function _getReplicationSources() {
25
+ return array('eav/attribute', 'mana_filters/filter2');
26
+ }
27
+ /**
28
+ * Enter description here ...
29
+ * @param Mana_Db_Model_Replication_Target $target
30
+ */
31
+ protected function _prepareReplicationUpdateSelects($target, $options) {
32
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
33
+ $select
34
+ ->from(array('eav_attribute' => Mage::getSingleton('core/resource')->getTableName('eav/attribute')), null)
35
+ ->joinInner(array('eav_attribute_option' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option')),
36
+ 'eav_attribute.attribute_id = eav_attribute_option.attribute_id', null)
37
+ ->joinInner(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
38
+ 'target.option_id = eav_attribute_option.option_id',
39
+ array('target.id AS id', 'target.option_id AS option_id', 'target.filter_id AS filter_id'))
40
+ ->joinInner(array('parent' => Mage::getSingleton('core/resource')->getTableName('mana_filters/filter2')),
41
+ 'target.filter_id = parent.id', null)
42
+ ->joinLeft(array('global_option_value' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option_value')),
43
+ 'global_option_value.option_id = eav_attribute_option.option_id AND global_option_value.store_id = 0', null)
44
+ ->distinct()
45
+ ->where('target.edit_status = 0')
46
+ ->columns(array(
47
+ 'target.default_mask0 AS default_mask0',
48
+ 'global_option_value.value_id AS value_id',
49
+ 'global_option_value.value AS name',
50
+ 'eav_attribute_option.sort_order AS position',
51
+ ));
52
+
53
+ if ($options['trackKeys']) {
54
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
55
+ $select->where('eav_attribute.attribute_id IN (?)', $keys);
56
+ $target->setIsKeyFilterApplied(true);
57
+ }
58
+ if (($keys = $options['targets'][$this->getEntityName()]->getSavedKeys()) && count($keys)) {
59
+ $select->where('target.id IN (?)', $keys);
60
+ $target->setIsKeyFilterApplied(true);
61
+ }
62
+ if (($keys = $options['targets']['mana_filters/filter2']->getSavedKeys()) && count($keys)) {
63
+ $select->where('parent.id IN (?)', $keys);
64
+ $target->setIsKeyFilterApplied(true);
65
+ }
66
+ }
67
+ $target->setSelect('main', $select);
68
+ }
69
+ /**
70
+ * Enter description here ...
71
+ * @param Mana_Db_Model_Object $object
72
+ * @param array $values
73
+ * @param array $options
74
+ */
75
+ protected function _processReplicationUpdate($object, $values, $options) {
76
+ $object
77
+ ->setId($values['id'])
78
+ ->setOptionId($values['option_id'])
79
+ ->setFilterId($values['filter_id'])
80
+ ->setValueId($values['value_id'])
81
+ ->setData('_m_prevent_replication', true);
82
+
83
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 0)) {
84
+ $object->setName($values['name']);
85
+ }
86
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 1)) {
87
+ $object->setPosition($values['position']);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Enter description here ...
93
+ * @param Mana_Db_Model_Replication_Target $target
94
+ */
95
+ protected function _prepareReplicationInsertSelects($target, $options) {
96
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
97
+ $select
98
+ ->from(array('eav_attribute' => Mage::getSingleton('core/resource')->getTableName('eav/attribute')), null)
99
+ ->joinInner(array('eav_attribute_option' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option')),
100
+ 'eav_attribute.attribute_id = eav_attribute_option.attribute_id',
101
+ 'eav_attribute_option.option_id AS option_id')
102
+ ->joinInner(array('parent' => Mage::getSingleton('core/resource')->getTableName('mana_filters/filter2')),
103
+ 'eav_attribute.attribute_code = parent.code',
104
+ 'parent.id AS filter_id')
105
+ ->joinLeft(array('global_option_value' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option_value')),
106
+ 'global_option_value.option_id = eav_attribute_option.option_id AND global_option_value.store_id = 0', null)
107
+ ->joinLeft(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
108
+ 'target.option_id = eav_attribute_option.option_id', null)
109
+ ->distinct()
110
+ ->where('target.id IS NULL')
111
+ ->columns(array(
112
+ 'global_option_value.value_id AS value_id',
113
+ 'global_option_value.value AS name',
114
+ 'eav_attribute_option.sort_order AS position',
115
+ ));
116
+
117
+ if ($options['trackKeys']) {
118
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
119
+ $select->where('eav_attribute.attribute_id IN (?)', $keys);
120
+ $target->setIsKeyFilterApplied(true);
121
+ }
122
+ if (($keys = $options['targets']['mana_filters/filter2']->getSavedKeys()) && count($keys)) {
123
+ $select->where('parent.id IN (?)', $keys);
124
+ $target->setIsKeyFilterApplied(true);
125
+ }
126
+ }
127
+ $target->setSelect('main', $select);
128
+ }
129
+ /**
130
+ * Enter description here ...
131
+ * @param Mana_Db_Model_Object $object
132
+ * @param array $values
133
+ * @param array $options
134
+ */
135
+ protected function _processReplicationInsert($object, $values, $options) {
136
+ $object
137
+ ->setOptionId($values['option_id'])
138
+ ->setFilterId($values['filter_id'])
139
+ ->setValueId($values['value_id'])
140
+ ->setData('_m_prevent_replication', true);
141
+
142
+ $object->setName($values['name']);
143
+ $object->setPosition($values['position']);
144
+ }
145
+ }
app/code/local/Mana/Filters/Resource/Filter2/Value/Collection.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* BASED ON SNIPPET: Resources/DB operations with model collections */
9
+ /**
10
+ * This resource model handles DB operations with a collection of models of type Mana_Filters_Model_Filter2_Value. All
11
+ * database specific code for operating collection of Mana_Filters_Model_Filter2_Value should go here.
12
+ * @author Mana Team
13
+ */
14
+ class Mana_Filters_Resource_Filter2_Value_Collection extends Mana_Db_Resource_Object_Collection
15
+ {
16
+ /**
17
+ * Invoked during resource collection model creation process, this method associates this
18
+ * resource collection model with model class and with resource model class
19
+ */
20
+ protected function _construct()
21
+ {
22
+ $this->_init(strtolower('Mana_Filters/Filter2_Value'));
23
+ }
24
+
25
+ }
app/code/local/Mana/Filters/Resource/Filter2/Value/Store.php ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* BASED ON SNIPPET: Resources/Single model DB operations */
9
+ /**
10
+ * This resource model handles DB operations with a single model of type Mana_Filters_Model_Filter2_Value_Store. All
11
+ * database specific code for Mana_Filters_Model_Filter2_Value_Store should go here.
12
+ * @author Mana Team
13
+ */
14
+ class Mana_Filters_Resource_Filter2_Value_Store extends Mana_Filters_Resource_Filter2_Value {
15
+ /**
16
+ * Invoked during resource model creation process, this method associates this resource model with model class
17
+ * and with DB table name
18
+ */
19
+ protected function _construct() {
20
+ $this->_init(strtolower('Mana_Filters/Filter2_Value_Store'), 'id');
21
+ $this->_isPkAutoIncrement = false;
22
+ }
23
+ protected function _getReplicationSources() {
24
+ return array('mana_filters/filter2_value', 'core/store', 'eav/attribute', 'mana_filters/filter2_store');
25
+ }
26
+ /**
27
+ * Enter description here ...
28
+ * @param Mana_Db_Model_Replication_Target $target
29
+ */
30
+ protected function _prepareReplicationUpdateSelects($target, $options) {
31
+ $globalEntityName = Mage::helper('mana_db')->getGlobalEntityName($this->getEntityName());
32
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
33
+ $select
34
+ ->from(array('global' => Mage::getSingleton('core/resource')->getTableName($globalEntityName)), null)
35
+ ->joinInner(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
36
+ 'target.global_id = global.id AND global.edit_status = 0',
37
+ array('target.id AS id', 'target.global_id AS global_id', 'target.store_id AS store_id',
38
+ 'target.filter_id AS filter_id'))
39
+ ->joinInner(array('eav_attribute_option' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option')),
40
+ 'global.option_id = eav_attribute_option.option_id', null)
41
+ ->joinInner(array('parent' => Mage::getSingleton('core/resource')->getTableName('mana_filters/filter2_store')),
42
+ 'target.filter_id = parent.id', null)
43
+ ->joinLeft(array('global_option_value' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option_value')),
44
+ 'global_option_value.option_id = eav_attribute_option.option_id AND global_option_value.store_id = 0', null)
45
+ ->joinLeft(array('store_option_value' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option_value')),
46
+ 'store_option_value.option_id = eav_attribute_option.option_id AND store_option_value.store_id = target.store_id', null)
47
+ ->distinct()
48
+ ->where('target.edit_status = 0')
49
+ ->columns(array(
50
+ 'target.default_mask0 AS default_mask0',
51
+ 'global.option_id AS option_id',
52
+ 'COALESCE(store_option_value.value_id, global_option_value.value_id) AS value_id',
53
+ "COALESCE(store_option_value.value, global_option_value.value, '') AS name",
54
+ 'global.position AS position',
55
+ ));
56
+
57
+ if ($options['trackKeys']) {
58
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
59
+ $select->where('eav_attribute_option.attribute_id IN (?)', $keys);
60
+ $target->setIsKeyFilterApplied(true);
61
+ }
62
+ if (($keys = $options['targets'][$globalEntityName]->getSavedKeys()) && count($keys)) {
63
+ $select->where('global.id IN (?)', $keys);
64
+ $target->setIsKeyFilterApplied(true);
65
+ }
66
+ if (($keys = $options['targets'][$this->getEntityName()]->getSavedKeys()) && count($keys)) {
67
+ $select->where('target.id IN (?)', $keys);
68
+ $target->setIsKeyFilterApplied(true);
69
+ }
70
+ if (($keys = $options['targets']['mana_filters/filter2_store']->getSavedKeys()) && count($keys)) {
71
+ $select->where('parent.id IN (?)', $keys);
72
+ $target->setIsKeyFilterApplied(true);
73
+ }
74
+ }
75
+ $target->setSelect('main', $select);
76
+ }
77
+ /**
78
+ * Enter description here ...
79
+ * @param Mana_Db_Model_Object $object
80
+ * @param array $values
81
+ * @param array $options
82
+ */
83
+ protected function _processReplicationUpdate($object, $values, $options) {
84
+ $object
85
+ ->setId($values['id'])
86
+ ->setGlobalId($values['global_id'])
87
+ ->setStoreId($values['store_id'])
88
+ ->setFilterId($values['filter_id'])
89
+ ->setOptionId($values['option_id'])
90
+ ->setValueId($values['value_id'])
91
+ ->setData('_m_prevent_replication', true);
92
+
93
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 0)) {
94
+ $object->setName($values['name']);
95
+ }
96
+ if (!Mage::helper('mana_db')->hasOverriddenValue($object, $values, 1)) {
97
+ $object->setPosition($values['position']);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Enter description here ...
103
+ * @param Mana_Db_Model_Replication_Target $target
104
+ */
105
+ protected function _prepareReplicationInsertSelects($target, $options) {
106
+ $globalEntityName = Mage::helper('mana_db')->getGlobalEntityName($this->getEntityName());
107
+ /* @var $select Varien_Db_Select */ $select = $options['db']->select();
108
+ $select
109
+ ->from(array('global' => Mage::getSingleton('core/resource')->getTableName($globalEntityName)), 'global.id AS global_id')
110
+ ->from(array('core_store' => Mage::getSingleton('core/resource')->getTableName('core_store')), 'core_store.store_id AS store_id')
111
+ ->joinInner(array('eav_attribute_option' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option')),
112
+ 'global.option_id = eav_attribute_option.option_id', null)
113
+ ->joinInner(array('eav_attribute' => Mage::getSingleton('core/resource')->getTableName('eav/attribute')),
114
+ 'eav_attribute.attribute_id = eav_attribute_option.attribute_id', null)
115
+ ->joinInner(array('parent_global' => Mage::getSingleton('core/resource')->getTableName('mana_filters/filter2')),
116
+ 'eav_attribute.attribute_code = parent_global.code', null)
117
+ ->joinInner(array('parent' => Mage::getSingleton('core/resource')->getTableName('mana_filters/filter2_store')),
118
+ 'parent_global.id = parent.global_id AND core_store.store_id = parent.store_id',
119
+ 'parent.id AS filter_id')
120
+ ->joinLeft(array('global_option_value' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option_value')),
121
+ 'global_option_value.option_id = eav_attribute_option.option_id AND global_option_value.store_id = 0', null)
122
+ ->joinLeft(array('store_option_value' => Mage::getSingleton('core/resource')->getTableName('eav/attribute_option_value')),
123
+ 'store_option_value.option_id = eav_attribute_option.option_id AND store_option_value.store_id = core_store.store_id', null)
124
+ ->joinLeft(array('target' => Mage::getSingleton('core/resource')->getTableName($this->getEntityName())),
125
+ 'target.global_id = global.id AND target.store_id = core_store.store_id', null)
126
+ ->distinct()
127
+ ->where('core_store.store_id <> 0')
128
+ ->where('global.edit_status = 0')
129
+ ->where('target.id IS NULL')
130
+ ->columns(array(
131
+ 'global.option_id AS option_id',
132
+ 'COALESCE(store_option_value.value_id, global_option_value.value_id) AS value_id',
133
+ "COALESCE(store_option_value.value, global_option_value.value, '') AS name",
134
+ 'global.position AS position',
135
+ ));
136
+
137
+ if ($options['trackKeys']) {
138
+ if (($keys = $options['targets']['eav/attribute']->getSavedKeys()) && count($keys)) {
139
+ $select->where('eav_attribute_option.attribute_id IN (?)', $keys);
140
+ $target->setIsKeyFilterApplied(true);
141
+ }
142
+ if (($keys = $options['targets'][$globalEntityName]->getSavedKeys()) && count($keys)) {
143
+ $select->where('global.id IN (?)', $keys);
144
+ $target->setIsKeyFilterApplied(true);
145
+ }
146
+ if (($keys = $options['targets']['mana_filters/filter2_store']->getSavedKeys()) && count($keys)) {
147
+ $select->where('parent.id IN (?)', $keys);
148
+ $target->setIsKeyFilterApplied(true);
149
+ }
150
+ }
151
+ $target->setSelect('main', $select);
152
+ }
153
+ /**
154
+ * Enter description here ...
155
+ * @param Mana_Db_Model_Object $object
156
+ * @param array $values
157
+ * @param array $options
158
+ */
159
+ protected function _processReplicationInsert($object, $values, $options) {
160
+ $object
161
+ ->setGlobalId($values['global_id'])
162
+ ->setStoreId($values['store_id'])
163
+ ->setFilterId($values['filter_id'])
164
+ ->setOptionId($values['option_id'])
165
+ ->setValueId($values['value_id'])
166
+ ->setData('_m_prevent_replication', true);
167
+
168
+ $object->setName($values['name']);
169
+ $object->setPosition($values['position']);
170
+ }
171
+ }
app/code/local/Mana/Filters/Resource/Filter2/Value/Store/Collection.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Resources/DB operations with model collections */
10
+ /**
11
+ * This resource model handles DB operations with a collection of models of type Mana_Filters_Model_Filter2_Value_Store. All
12
+ * database specific code for operating collection of Mana_Filters_Model_Filter2_Value_Store should go here.
13
+ * @author Mana Team
14
+ */
15
+ class Mana_Filters_Resource_Filter2_Value_Store_Collection extends Mana_Filters_Resource_Filter2_Value_Collection
16
+ {
17
+ /**
18
+ * Invoked during resource collection model creation process, this method associates this
19
+ * resource collection model with model class and with resource model class
20
+ */
21
+ protected function _construct()
22
+ {
23
+ $this->_init(strtolower('Mana_Filters/Filter2_Value_Store'));
24
+ }
25
+
26
+ }
app/code/local/Mana/Filters/Resource/Setup.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /**
10
+ * Eav enabled class which is used by setup scripts of this module
11
+ * @author Mana Team
12
+ *
13
+ */
14
+ class Mana_Filters_Resource_Setup extends Mana_Core_Resource_Eav_Setup {
15
+ public function getDefaultEntities() {
16
+ return array('m_filter' => array(
17
+ 'entity_model' => 'mana_filters/filter', // model class
18
+ 'table' => 'mana_filters/filter', // base table name
19
+ 'additional_attribute_table' => 'mana_core/attribute',
20
+ 'entity_attribute_collection' => 'mana_filters/filter_attribute_collection',
21
+
22
+ 'attribute_model' => '', // 1.6 fix
23
+ 'increment_model' => '', // 1.6 fix
24
+ 'table_prefix' => '', // 1.6 fix
25
+ 'id_field' => '', // 1.6 fix
26
+ 'attributes' => array(
27
+ 'is_enabled' => array(
28
+ // storage
29
+ 'type' => 'int',
30
+ 'default' => '',
31
+ 'is_global' => Mana_Core_Model_Attribute_Scope::_STORE,
32
+
33
+ // editing
34
+ 'label' => 'Is Visible In Category',
35
+ 'input' => 'select', // dropdown combobox
36
+ 'source' => 'mana_filters/source_filterable',
37
+ 'required' => true,
38
+
39
+ // default chain
40
+ 'has_default' => true,
41
+ 'default_model' => 'mana_filters/filter_default',
42
+ 'default_mask' => 0x0000000000000001,
43
+ ),
44
+ 'is_enabled_in_search' => array(
45
+ // storage
46
+ 'type' => 'int',
47
+ 'default' => '',
48
+ 'is_global' => Mana_Core_Model_Attribute_Scope::_STORE,
49
+
50
+ // editing
51
+ 'label' => 'Is Visible In Search',
52
+ 'input' => 'select', // dropdown combobox
53
+ 'source' => 'mana_filters/source_filterable',
54
+ 'required' => true,
55
+
56
+ // default chain
57
+ 'has_default' => true,
58
+ 'default_model' => 'mana_filters/filter_default',
59
+ 'default_mask' => 0x0000000000000010,
60
+ ),
61
+ 'position' => array(
62
+ // storage
63
+ 'type' => 'int',
64
+ 'default' => '',
65
+ 'is_global' => Mana_Core_Model_Attribute_Scope::_STORE,
66
+
67
+ // editing
68
+ 'label' => 'Position',
69
+ 'note' => 'All filters are shown in layered navigation ordered by position',
70
+ 'input' => 'text', // dropdown combobox
71
+ 'required' => true,
72
+
73
+ // default chain
74
+ 'has_default' => true,
75
+ 'default_model' => 'mana_filters/filter_default',
76
+ 'default_mask' => 0x0000000000000020,
77
+ ),
78
+ 'display' => array(
79
+ // storage
80
+ 'type' => 'varchar',
81
+ 'default' => '',
82
+ 'is_global' => Mana_Core_Model_Attribute_Scope::_STORE,
83
+
84
+ // editing
85
+ 'label' => 'Display As',
86
+ 'input' => 'select', // dropdown combobox
87
+ 'source' => 'mana_filters/source_display',
88
+ 'required' => true,
89
+
90
+ // default chain
91
+ 'has_default' => true,
92
+ 'default_model' => 'mana_filters/config_display_default',
93
+ 'default_source' => 'mana_filters/display/%s',
94
+ 'default_mask' => 0x0000000000000002,
95
+ ),
96
+ 'code' => array(
97
+ // storage
98
+ 'type' => 'static',
99
+ 'default' => '',
100
+ 'is_global' => Mana_Core_Model_Attribute_Scope::_GLOBAL,
101
+ 'is_key' => true,
102
+
103
+ // editing
104
+ 'input' => 'hidden',
105
+ ),
106
+ 'default_mask0' => array(
107
+ // storage
108
+ 'type' => 'static',
109
+ 'default' => '',
110
+ 'is_global' => Mana_Core_Model_Attribute_Scope::_GLOBAL,
111
+
112
+ // editing
113
+ 'input' => 'hidden',
114
+ ),
115
+ 'name' => array(
116
+ // storage
117
+ 'type' => 'varchar',
118
+ 'default' => '',
119
+ 'is_global' => Mana_Core_Model_Attribute_Scope::_STORE,
120
+
121
+ // editing
122
+ 'label' => 'Name',
123
+ 'input' => 'text',
124
+ 'required' => true,
125
+
126
+ // default chain
127
+ 'has_default' => true,
128
+ 'default_model' => 'mana_filters/filter_default',
129
+ 'default_mask' => 0x0000000000000004,
130
+ ),
131
+ ),
132
+ ));
133
+ }
134
+ }
app/code/local/Mana/Filters/etc/config.xml CHANGED
@@ -12,7 +12,7 @@
12
  <Mana_Filters>
13
  <!-- This version number identifies version of database tables specific to this extension. It is written to
14
  core_resource table. -->
15
- <version>1.1.0</version>
16
  </Mana_Filters>
17
  </modules>
18
 
@@ -65,13 +65,56 @@
65
  <mana_filters_resources>
66
  <class>Mana_Filters_Resource</class>
67
  <entities>
 
 
 
 
 
68
  <!-- INSERT HERE: table-entity mappings -->
69
  </entities>
70
  </mana_filters_resources>
71
 
72
- <!-- INSERT HERE: rewrites, ... -->
73
  </models>
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  <!-- INSERT HERE: blocks, models, ... -->
76
  </global>
77
 
@@ -121,35 +164,93 @@
121
  </adminhtml>
122
  <!-- INSERT HERE: adminhtml, frontend, ... -->
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  <!-- This section enumerates configuration based extensibility points and provides default entries -->
125
  <mana_filters>
126
  <display><!-- display options for individual filters -->
127
  <attribute> <!-- available display options for attribute-based filters -->
128
  <!-- by default, filter items are displayed as HTML list -->
129
  <list translate="title" module="mana_filters">
130
- <title>List</title><!-- this one is displayed in admin -->
 
 
 
131
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
132
- <position>100</position><!-- Position in "Display as" list. The item with least position is the default one. -->
133
  </list>
 
 
 
 
 
 
 
 
134
  </attribute>
135
  <price> <!-- available display options for price filter -->
136
  <!-- by default, filter items are displayed as HTML list -->
137
  <list translate="title" module="mana_filters">
138
- <title>List</title><!-- this one is displayed in admin -->
 
 
 
139
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
140
- <position>100</position><!-- Position in "Display as" list. The item with least position is the default one. -->
141
  </list>
 
 
 
 
 
 
 
 
142
  </price>
143
  <category> <!-- available display options for category filter -->
 
 
 
 
 
 
 
 
 
 
144
  <!-- by default, filter items are displayed as HTML list -->
145
  <list translate="title" module="mana_filters">
146
- <title>List</title><!-- this one is displayed in admin -->
 
 
 
147
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
148
- <position>100</position><!-- Position in "Display as" list. The item with least position is the default one. -->
149
  </list>
150
- </category>
151
- <decimal> <!-- available display options for decimal filters -->
152
- <!-- No entries here. We do not know how to handle this by default -->
 
 
 
 
 
153
  </decimal>
154
  </display>
155
 
12
  <Mana_Filters>
13
  <!-- This version number identifies version of database tables specific to this extension. It is written to
14
  core_resource table. -->
15
+ <version>12.04.21.23</version>
16
  </Mana_Filters>
17
  </modules>
18
 
65
  <mana_filters_resources>
66
  <class>Mana_Filters_Resource</class>
67
  <entities>
68
+ <filter><table>m_filter</table></filter>
69
+ <filter2><table>m_filter2</table><replicable>1</replicable></filter2>
70
+ <filter2_store><table>m_filter2_store</table><replicable>1</replicable></filter2_store>
71
+ <filter2_value><table>m_filter2_value</table><replicable>1</replicable></filter2_value>
72
+ <filter2_value_store><table>m_filter2_value_store</table><replicable>1</replicable></filter2_value_store>
73
  <!-- INSERT HERE: table-entity mappings -->
74
  </entities>
75
  </mana_filters_resources>
76
 
77
+ <!-- INSERT HERE: rewrites, ... -->
78
  </models>
79
 
80
+ <!-- BASED ON SNIPPET: Resources/Installation script support (config.xml) -->
81
+ <!-- This tells Magento to analyze sql/mana_filters_setup directory for install/upgrade scripts.
82
+ Installation scripts should be named as 'mysql4-install-<new version>.php'.
83
+ Upgrade scripts should be named as mysql4-upgrade-<current version>-<new version>.php -->
84
+ <resources>
85
+ <mana_filters_setup>
86
+ <setup>
87
+ <module>Mana_Filters</module>
88
+ <class>Mana_Filters_Resource_Setup</class>
89
+ </setup>
90
+ </mana_filters_setup>
91
+ </resources>
92
+
93
+ <!-- BASED ON SNIPPET: New Models/Event support (config.xml) -->
94
+ <!-- This section registers event handlers of this module defined in Mana_Filters_Model_Observer with events defined in other
95
+ module throughout the system. So when some code in other module invokes an event mentioned in this section, handler
96
+ method of Mana_Filters_Model_Observer class gets called. -->
97
+ <events>
98
+ <!-- BASED ON SNIPPET: Models/Event handler (config.xml) -->
99
+ <m_db_is_config_changed><!-- this is event name this module listens for -->
100
+ <observers>
101
+ <mana_filters>
102
+ <class>mana_filters/observer</class> <!-- model name of class containing event handler methods -->
103
+ <method>isConfigChanged</method> <!-- event handler method name -->
104
+ </mana_filters>
105
+ </observers>
106
+ </m_db_is_config_changed>
107
+ <catalog_entity_attribute_save_commit_after><!-- this is event name this module listens for -->
108
+ <observers>
109
+ <mana_filters>
110
+ <class>mana_filters/observer</class>
111
+ <!-- model name of class containing event handler methods -->
112
+ <method>afterCatalogAttributeSave</method>
113
+ <!-- event handler method name -->
114
+ </mana_filters>
115
+ </observers>
116
+ </catalog_entity_attribute_save_commit_after>
117
+ </events>
118
  <!-- INSERT HERE: blocks, models, ... -->
119
  </global>
120
 
164
  </adminhtml>
165
  <!-- INSERT HERE: adminhtml, frontend, ... -->
166
 
167
+ <!-- This section provides default values for global configuration -->
168
+ <default>
169
+ <mana_filters>
170
+ <display>
171
+ <attribute>list</attribute>
172
+ <price>list</price>
173
+ <category>standard</category>
174
+ <decimal>list</decimal>
175
+ </display>
176
+ <general>
177
+ <is_multiselect>1</is_multiselect>
178
+ <adjust_configurable_price>1</adjust_configurable_price>
179
+ </general>
180
+ <session>
181
+ <save_applied_filters>0</save_applied_filters>
182
+ </session>
183
+ </mana_filters>
184
+ </default>
185
  <!-- This section enumerates configuration based extensibility points and provides default entries -->
186
  <mana_filters>
187
  <display><!-- display options for individual filters -->
188
  <attribute> <!-- available display options for attribute-based filters -->
189
  <!-- by default, filter items are displayed as HTML list -->
190
  <list translate="title" module="mana_filters">
191
+ <title>Text (Multiple Select Enabled)</title><!-- this one is displayed in admin -->
192
+ <block>mana_filters/filter</block>
193
+ <model>mana_filters/filter_attribute</model>
194
+ <resource>mana_filters/filter_attribute</resource>
195
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
196
+ <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
197
  </list>
198
+ <standard translate="title" module="mana_filters">
199
+ <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
200
+ <block>mana_filters/filter</block>
201
+ <model>mana_filters/filter_attribute</model>
202
+ <resource>mana_filters/filter_attribute</resource>
203
+ <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
204
+ <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
205
+ </standard>
206
  </attribute>
207
  <price> <!-- available display options for price filter -->
208
  <!-- by default, filter items are displayed as HTML list -->
209
  <list translate="title" module="mana_filters">
210
+ <title>Text (Multiple Select Enabled)</title><!-- this one is displayed in admin -->
211
+ <block>mana_filters/filter</block>
212
+ <model>mana_filters/filter_price</model>
213
+ <resource>mana_filters/filter_price</resource>
214
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
215
+ <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
216
  </list>
217
+ <standard translate="title" module="mana_filters">
218
+ <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
219
+ <block>mana_filters/filter</block>
220
+ <model>mana_filters/filter_price</model>
221
+ <resource>mana_filters/filter_price</resource>
222
+ <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
223
+ <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
224
+ </standard>
225
  </price>
226
  <category> <!-- available display options for category filter -->
227
+ <!-- by default, filter items are displayed as HTML list -->
228
+ <standard translate="title" module="mana_filters">
229
+ <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
230
+ <block>mana_filters/filter</block>
231
+ <model>mana_filters/filter_category</model>
232
+ <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
233
+ <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
234
+ </standard>
235
+ </category>
236
+ <decimal> <!-- available display options for decimal filters -->
237
  <!-- by default, filter items are displayed as HTML list -->
238
  <list translate="title" module="mana_filters">
239
+ <title>Text (Multiple Select Enabled)</title><!-- this one is displayed in admin -->
240
+ <block>mana_filters/filter</block>
241
+ <model>mana_filters/filter_decimal</model>
242
+ <resource>mana_filters/filter_decimal</resource>
243
  <template>mana/filters/items/list.phtml</template><!-- PHTML to be rendered -->
244
+ <sort_order>50</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
245
  </list>
246
+ <standard translate="title" module="mana_filters">
247
+ <title>Text (One Item Can Be Selected At A Time)</title><!-- this one is displayed in admin -->
248
+ <block>mana_filters/filter</block>
249
+ <model>mana_filters/filter_decimal</model>
250
+ <resource>mana_filters/filter_decimal</resource>
251
+ <template>mana/filters/items/standard.phtml</template><!-- PHTML to be rendered -->
252
+ <sort_order>100</sort_order><!-- Position in "Display as" list. The item with least position is the default one. -->
253
+ </standard>
254
  </decimal>
255
  </display>
256
 
app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-install-1.1.1.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
9
+ /* @var $installer Mana_Filters_Resource_Setup */
10
+ $installer = $this;
11
+
12
+ $installer->startSetup();
13
+
14
+ $installer->installEntities();
15
+
16
+ $installer->createEntityTables($this->getTable('mana_filters/filter'));
17
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
18
+ $table = 'mana_filters/filter';
19
+ $installer->run("
20
+ ALTER TABLE `{$this->getTable($table)}` DROP FOREIGN KEY `FK_{$this->getTable($table)}_store`;
21
+ ALTER TABLE `{$this->getTable($table)}` DROP COLUMN `store_id`;
22
+ ALTER TABLE `{$this->getTable($table)}` DROP COLUMN `increment_id`;
23
+
24
+ ALTER TABLE `{$this->getTable($table)}` ADD COLUMN (
25
+ `code` varchar(255) NOT NULL default ''
26
+ );
27
+ ALTER TABLE `{$this->getTable($table)}` ADD KEY `code` (`code`);
28
+ ");
29
+
30
+ $installer->updateDefaultMaskFields(Mana_Filters_Model_Filter::ENTITY);
31
+
32
+ $installer->endSetup();
app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-1.1.1-1.9.1.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+
9
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
10
+ /* @var $installer Mana_Filters_Resource_Setup */
11
+ $installer = $this;
12
+
13
+ $installer->startSetup();
14
+
15
+ $installer->installEntities();
16
+ $installer->updateDefaultMaskFields(Mana_Filters_Model_Filter::ENTITY);
17
+
18
+ $installer->endSetup();
app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-11.09.24.09-11.09.28.09.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
4
+ if (defined('COMPILER_INCLUDE_PATH')) {
5
+ throw new Exception(Mage::helper('mana_core')->__('This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.'));
6
+ }
7
+
8
+ /* @var $installer Mage_Core_Model_Resource_Setup */
9
+ $installer = $this;
10
+
11
+ $installer->startSetup();
12
+
13
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
14
+ $table = 'm_filter2';
15
+ $installer->run("
16
+ DROP TABLE IF EXISTS `{$this->getTable($table)}`;
17
+ CREATE TABLE `{$this->getTable($table)}` (
18
+ `id` bigint NOT NULL AUTO_INCREMENT,
19
+ `default_mask0` int unsigned NOT NULL default '0',
20
+ `code` varchar(255) NOT NULL,
21
+ `type` varchar(20) NOT NULL,
22
+
23
+ `is_enabled` tinyint NOT NULL default '0',
24
+ `display` varchar(255) NOT NULL default '',
25
+ `name` varchar(255) NOT NULL default '',
26
+ `is_enabled_in_search` tinyint NOT NULL default '0',
27
+ `position` int NOT NULL default '0',
28
+
29
+ PRIMARY KEY (`id`),
30
+ UNIQUE KEY `code` (`code`)
31
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
32
+
33
+ ");
34
+
35
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
36
+ $table = 'm_filter2_store';
37
+ $installer->run("
38
+ DROP TABLE IF EXISTS `{$this->getTable($table)}`;
39
+ CREATE TABLE `{$this->getTable($table)}` (
40
+ `id` bigint NOT NULL AUTO_INCREMENT,
41
+ `default_mask0` int unsigned NOT NULL default '0',
42
+ `global_id` bigint NOT NULL,
43
+ `store_id` smallint(5) unsigned NOT NULL,
44
+
45
+ `is_enabled` tinyint NOT NULL default '0',
46
+ `display` varchar(255) NOT NULL default '',
47
+ `name` varchar(255) NOT NULL default '',
48
+ `is_enabled_in_search` tinyint NOT NULL default '0',
49
+ `position` int NOT NULL default '0',
50
+
51
+ PRIMARY KEY (`id`),
52
+ KEY `global_id` (`global_id`),
53
+ KEY `store_id` (`store_id`)
54
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
55
+
56
+ ALTER TABLE `{$this->getTable($table)}`
57
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_mana_filters/filter2` FOREIGN KEY (`global_id`)
58
+ REFERENCES `{$installer->getTable('mana_filters/filter2')}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
59
+
60
+ ALTER TABLE `{$this->getTable($table)}`
61
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_core/store` FOREIGN KEY (`store_id`)
62
+ REFERENCES `{$installer->getTable('core/store')}` (`store_id`) ON DELETE CASCADE ON UPDATE CASCADE;
63
+ ");
64
+
65
+ $installer->endSetup();
66
+
67
+ if (!Mage::registry('m_run_db_replication')) {
68
+ Mage::register('m_run_db_replication', true);
69
+ }
app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-11.10.19.18-11.10.23.01.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
4
+ if (defined('COMPILER_INCLUDE_PATH')) {
5
+ throw new Exception(Mage::helper('mana_core')->__('This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.'));
6
+ }
7
+
8
+ /* @var $installer Mage_Core_Model_Resource_Setup */
9
+ $installer = $this;
10
+
11
+ $installer->startSetup();
12
+
13
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
14
+ $table = 'm_filter2_value';
15
+ $installer->run("
16
+ DROP TABLE IF EXISTS `{$this->getTable($table)}`;
17
+ CREATE TABLE `{$this->getTable($table)}` (
18
+ `id` bigint NOT NULL AUTO_INCREMENT,
19
+ `default_mask0` int unsigned NOT NULL default '0',
20
+ `edit_session_id` bigint NOT NULL default '0',
21
+ `edit_status` bigint NOT NULL default '0',
22
+
23
+
24
+ `filter_id` bigint NOT NULL,
25
+ `option_id` int(10) unsigned NOT NULL,
26
+ `value_id` int(10) unsigned NULL,
27
+
28
+ `name` varchar(255) NOT NULL default '',
29
+ `position` smallint(5) NOT NULL default '0',
30
+
31
+ PRIMARY KEY (`id`),
32
+ KEY `filter_id` (`filter_id`),
33
+ KEY `option_id` (`option_id`),
34
+ KEY `value_id` (`value_id`),
35
+ KEY `edit_session_id` (`edit_session_id`),
36
+ KEY `edit_status` (`edit_status`)
37
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
38
+
39
+ ALTER TABLE `{$this->getTable($table)}`
40
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_mana_db/edit_session` FOREIGN KEY (`edit_session_id`)
41
+ REFERENCES `{$installer->getTable('mana_db/edit_session')}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
42
+
43
+ ALTER TABLE `{$this->getTable($table)}`
44
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_mana_filters/filter2` FOREIGN KEY (`filter_id`)
45
+ REFERENCES `{$installer->getTable('mana_filters/filter2')}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
46
+
47
+ ALTER TABLE `{$this->getTable($table)}`
48
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_eav/attribute_option` FOREIGN KEY (`option_id`)
49
+ REFERENCES `{$installer->getTable('eav/attribute_option')}` (`option_id`) ON DELETE CASCADE ON UPDATE CASCADE;
50
+
51
+ ALTER TABLE `{$this->getTable($table)}`
52
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_eav/attribute_option_value` FOREIGN KEY (`value_id`)
53
+ REFERENCES `{$installer->getTable('eav/attribute_option_value')}` (`value_id`) ON DELETE SET NULL ON UPDATE SET NULL;
54
+
55
+ ");
56
+
57
+ /* BASED ON SNIPPET: Resources/Table creation/alteration script */
58
+ $table = 'm_filter2_value_store';
59
+ $installer->run("
60
+ DROP TABLE IF EXISTS `{$this->getTable($table)}`;
61
+ CREATE TABLE `{$this->getTable($table)}` (
62
+ `id` bigint NOT NULL AUTO_INCREMENT,
63
+ `global_id` bigint NOT NULL,
64
+ `store_id` smallint(5) unsigned NOT NULL,
65
+ `default_mask0` int unsigned NOT NULL default '0',
66
+ `edit_session_id` bigint NOT NULL default '0',
67
+ `edit_status` bigint NOT NULL default '0',
68
+
69
+ `filter_id` bigint NOT NULL,
70
+ `option_id` int(10) unsigned NOT NULL,
71
+ `value_id` int(10) unsigned NULL,
72
+
73
+ `name` varchar(255) NOT NULL default '',
74
+ `position` smallint(5) NOT NULL default '0',
75
+
76
+ PRIMARY KEY (`id`),
77
+ KEY `filter_id` (`filter_id`),
78
+ KEY `option_id` (`option_id`),
79
+ KEY `value_id` (`value_id`),
80
+ KEY `edit_session_id` (`edit_session_id`),
81
+ KEY `edit_status` (`edit_status`)
82
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
83
+
84
+ ALTER TABLE `{$this->getTable($table)}`
85
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_mana_db/edit_session` FOREIGN KEY (`edit_session_id`)
86
+ REFERENCES `{$installer->getTable('mana_db/edit_session')}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
87
+
88
+ ALTER TABLE `{$this->getTable($table)}`
89
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_mana_filters/filter2_store` FOREIGN KEY (`filter_id`)
90
+ REFERENCES `{$installer->getTable('mana_filters/filter2_store')}` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
91
+
92
+ ALTER TABLE `{$this->getTable($table)}`
93
+ ADD CONSTRAINT `FK_{$this->getTable($table)}_eav/attribute_option_value` FOREIGN KEY (`value_id`)
94
+ REFERENCES `{$installer->getTable('eav/attribute_option_value')}` (`value_id`) ON DELETE SET NULL ON UPDATE SET NULL;
95
+ ");
96
+
97
+ $installer->endSetup();
98
+
99
+ if (!Mage::registry('m_run_db_replication')) {
100
+ Mage::register('m_run_db_replication', true);
101
+ }
app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-12.01.14.09-12.01.15.14.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
4
+ if (defined('COMPILER_INCLUDE_PATH')) {
5
+ throw new Exception(Mage::helper('mana_core')->__('This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.'));
6
+ }
7
+
8
+ /* @var $installer Mage_Core_Model_Resource_Setup */
9
+ $installer = $this;
10
+ if (method_exists($this->getConnection(), 'allowDdlCache')) {
11
+ $this->getConnection()->allowDdlCache();
12
+ }
13
+
14
+ $table = 'mana_filters/filter2';
15
+ $installer->run("
16
+ ALTER TABLE `{$this->getTable($table)}` ADD COLUMN (
17
+ `sort_method` varchar(50) NOT NULL default ''
18
+ );
19
+ ");
20
+
21
+ $table = 'mana_filters/filter2_store';
22
+ $installer->run("
23
+ ALTER TABLE `{$this->getTable($table)}` ADD COLUMN (
24
+ `sort_method` varchar(50) NOT NULL default ''
25
+ );
26
+ ");
27
+
28
+ if (method_exists($this->getConnection(), 'disallowDdlCache')) {
29
+ $this->getConnection()->disallowDdlCache();
30
+ }
31
+ $installer->endSetup();
32
+
app/code/local/Mana/Filters/sql/mana_filters_setup/mysql4-upgrade-12.04.10.23-12.04.17.18.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* BASED ON SNIPPET: Resources/Install/upgrade script */
4
+ if (defined('COMPILER_INCLUDE_PATH')) {
5
+ throw new Exception(Mage::helper('mana_core')->__('This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.'));
6
+ }
7
+
8
+ /* @var $installer Mage_Core_Model_Resource_Setup */
9
+ $installer = $this;
10
+ if (method_exists($this->getConnection(), 'allowDdlCache')) {
11
+ $this->getConnection()->allowDdlCache();
12
+ }
13
+
14
+ $table = 'mana_filters/filter2';
15
+ $installer->run("
16
+ ALTER TABLE `{$this->getTable($table)}` ADD COLUMN (
17
+ `operation` varchar(10) NOT NULL default ''
18
+ );
19
+ ");
20
+
21
+ $table = 'mana_filters/filter2_store';
22
+ $installer->run("
23
+ ALTER TABLE `{$this->getTable($table)}` ADD COLUMN (
24
+ `operation` varchar(10) NOT NULL default ''
25
+ );
26
+ ");
27
+
28
+ if (method_exists($this->getConnection(), 'disallowDdlCache')) {
29
+ $this->getConnection()->disallowDdlCache();
30
+ }
31
+ $installer->endSetup();
32
+
app/design/adminhtml/default/default/layout/mana_core.xml ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ @category Mana
4
+ @package Mana_Core
5
+ @copyright Copyright (c) http://www.manadev.com
6
+ @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ -->
8
+ <!-- BASED ON SNIPPET: Static Visuals/Empty layout file -->
9
+ <!-- This file defines the rules which should be applied when module mana_core is installed and active. Typically,
10
+ rules consists of the following parts:
11
+ 1. You say on which types of pages you would like your changes to be applied (in Magento wording, you need to
12
+ specify layout handle), for example, layout handle "catalog_category_layered" selects all pages where
13
+ specific category products are shown and where layered navigation is enabled. Layout handle "default" selects
14
+ every each page rendered by Magento.
15
+ 2. You say in which blocks you would like to make the changes (in Magento wording you reference parent block).
16
+ 3. You say what changes you would like to apply to that block (you could specify to remove child blocks, to add
17
+ your own blocks, to invoke methods on referenced block).
18
+ Review standard Magento layout XML's for full list of available layout handles, blocks to be referenced, and for
19
+ examples on what kind of actions can be applied to referenced blocks.
20
+ -->
21
+ <layout version="0.1.0">
22
+ <default>
23
+ <reference name="before_body_end">
24
+ <block type="mana_core/singleton" name="m_core_singletons" />
25
+ </reference>
26
+ </default>
27
+ <jquery_core>
28
+ <reference name="head">
29
+ <action method="addJs" ifconfig="mana_dev/debug/jquery"><script>jquery/jquery.js</script></action>
30
+ <action method="addJs" ifconfig="mana_dev/debug/jquery_min"><script>jquery/jquery.min.js</script></action>
31
+ <action method="addJs"><script>jquery/jquery.printf.js</script></action>
32
+ <action method="addJs"><script>mana/core.js</script></action>
33
+ <action method="addCss"><stylesheet>css/mana_core.css</stylesheet></action>
34
+ </reference>
35
+ <reference name="m_core_singletons">
36
+ <action method="addSingletonBlock">
37
+ <type>mana_core/js</type>
38
+ <name>m_js</name>
39
+ <template>mana/core/js.phtml</template>
40
+ </action>
41
+ <action method="addSingletonBlock">
42
+ <type>core/template</type>
43
+ <name>m_wait</name>
44
+ <template>mana/core/wait.phtml</template>
45
+ </action>
46
+ </reference>
47
+ </jquery_core>
48
+ <jquery_basic_effects>
49
+ <reference name="head">
50
+ <action method="addJs"><script>jquery/jquery.easing.js</script></action>
51
+ <action method="addJs"><script>jquery/advListRotator.js</script></action>
52
+ </reference>
53
+ </jquery_basic_effects>
54
+ <jquery_ui>
55
+ <reference name="head">
56
+ <action method="addCss"><stylesheet>css/jquery/ui.css</stylesheet></action>
57
+ <action method="addJs" ifconfig="mana_dev/debug/jquery"><script>jquery/jquery-ui.js</script></action>
58
+ <action method="addJs" ifconfig="mana_dev/debug/jquery_min"><script>jquery/jquery-ui.min.js</script></action>
59
+ </reference>
60
+ </jquery_ui>
61
+ </layout>
app/design/adminhtml/default/default/template/mana/core/js.phtml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* @var $this Mana_Core_Block_Js */
9
+ ?>
10
+ <script type="text/javascript">
11
+ //<![CDATA[
12
+ (function($) {
13
+ <?php if ($_translations = $this->getTranslations()) : ?>
14
+ $.__(<?php echo json_encode($_translations) ?>);
15
+ <?php endif; ?>
16
+ <?php if ($_options = $this->getOptions()) : ?>
17
+ $.options(<?php echo json_encode($_options) ?>);
18
+ <?php endif; ?>
19
+ })(jQuery);
20
+ //]]>
21
+ </script>
app/design/adminhtml/default/default/template/mana/core/wait.phtml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* @var $this Mage_Core_Block_Template */
9
+ ?>
10
+ <div id="m-wait" style="display:none">
11
+ <p class="loader" id="loading_mask_loader"><img src="<?php echo $this->getSkinUrl('images/mana_core/m-wait.gif') ?>" alt="<?php echo Mage::helper('adminhtml')->__('Loading...') ?>"/><br/><?php echo Mage::helper('adminhtml')->__('Please wait...') ?></p>
12
+ </div>
13
+
app/design/frontend/base/default/layout/mana_core.xml ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ @category Mana
4
+ @package Mana_Core
5
+ @copyright Copyright (c) http://www.manadev.com
6
+ @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ -->
8
+ <!-- BASED ON SNIPPET: Static Visuals/Empty layout file -->
9
+ <!-- This file defines the rules which should be applied when module mana_core is installed and active. Typically,
10
+ rules consists of the following parts:
11
+ 1. You say on which types of pages you would like your changes to be applied (in Magento wording, you need to
12
+ specify layout handle), for example, layout handle "catalog_category_layered" selects all pages where
13
+ specific category products are shown and where layered navigation is enabled. Layout handle "default" selects
14
+ every each page rendered by Magento.
15
+ 2. You say in which blocks you would like to make the changes (in Magento wording you reference parent block).
16
+ 3. You say what changes you would like to apply to that block (you could specify to remove child blocks, to add
17
+ your own blocks, to invoke methods on referenced block).
18
+ Review standard Magento layout XML's for full list of available layout handles, blocks to be referenced, and for
19
+ examples on what kind of actions can be applied to referenced blocks.
20
+ -->
21
+ <layout version="0.1.0">
22
+ <default>
23
+ <reference name="before_body_end">
24
+ <block type="mana_core/singleton" name="m_core_singletons"/>
25
+ </reference>
26
+ </default>
27
+ <jquery_core>
28
+ <reference name="head">
29
+ <action method="addJs" ifconfig="mana_dev/debug/jquery"><script>jquery/jquery.js</script></action>
30
+ <action method="addJs" ifconfig="mana_dev/debug/jquery_min"><script>jquery/jquery.min.js</script></action>
31
+ <action method="addJs"><script>jquery/jquery.printf.js</script></action>
32
+ <action method="addJs"><script>mana/core.js</script></action>
33
+ <action method="addCss"><stylesheet>css/mana_core.css</stylesheet></action>
34
+ </reference>
35
+ <reference name="m_core_singletons">
36
+ <action method="addSingletonBlock">
37
+ <type>mana_core/js</type>
38
+ <name>m_js</name>
39
+ <template>mana/core/js.phtml</template>
40
+ </action>
41
+ <action method="addSingletonBlock">
42
+ <type>core/template</type>
43
+ <name>m_wait</name>
44
+ <template>mana/core/wait.phtml</template>
45
+ </action>
46
+ <action method="addSingletonBlock">
47
+ <type>core/template</type>
48
+ <name>m_popup</name>
49
+ <template>mana/core/popup.phtml</template>
50
+ </action>
51
+ </reference>
52
+ </jquery_core>
53
+ <jquery_basic_effects>
54
+ <reference name="head">
55
+ <action method="addJs"><script>jquery/jquery.easing.js</script></action>
56
+ <action method="addJs"><script>jquery/advListRotator.js</script></action>
57
+ </reference>
58
+ </jquery_basic_effects>
59
+ <jquery_ui>
60
+ <reference name="head">
61
+ <action method="addCss"><stylesheet>css/jquery/ui.css</stylesheet></action>
62
+ <action method="addJs" ifconfig="mana_dev/debug/jquery"><script>jquery/jquery-ui.js</script></action>
63
+ <action method="addJs" ifconfig="mana_dev/debug/jquery_min"><script>jquery/jquery-ui.min.js</script></action>
64
+ <action method="addJs"><script>jquery/validate/jquery.validate.js</script></action>
65
+ </reference>
66
+ </jquery_ui>
67
+ </layout>
app/design/frontend/base/default/layout/mana_filters.xml CHANGED
@@ -22,13 +22,21 @@ examples on what kind of actions can be applied to referenced blocks.
22
  <catalog_category_layered> <!-- find all category pages with layered navigation -->
23
  <reference name="left"> <!-- find left column block -->
24
  <remove name="catalog.leftnav"/> <!-- remove standard layered navigation -->
25
- <block type="mana_filters/view_category" name="mana.catalog.leftnav" after="currency" template="catalog/layer/view.phtml"/>
 
 
 
 
26
  </reference>
27
  </catalog_category_layered>
28
  <catalogsearch_result_index> <!-- find all catalog search result page -->
29
  <reference name="left"> <!-- find left column block -->
30
  <remove name="catalogsearch.leftnav"/> <!-- remove standard layered navigation -->
31
- <block type="mana_filters/view_search" name="mana.catalogsearch.leftnav" after="currency" template="catalog/layer/view.phtml"/>
 
 
 
 
32
  </reference>
33
  </catalogsearch_result_index>
34
  </layout>
22
  <catalog_category_layered> <!-- find all category pages with layered navigation -->
23
  <reference name="left"> <!-- find left column block -->
24
  <remove name="catalog.leftnav"/> <!-- remove standard layered navigation -->
25
+ <remove name="enterprisecatalog.leftnav"/> <!-- remove enterprise layered navigation -->
26
+ <block type="mana_filters/view" name="mana.catalog.leftnav" before="-" template="catalog/layer/view.phtml"/>
27
+ </reference>
28
+ <reference name="head">
29
+ <action method="addCss"><stylesheet>css/mana_filters.css</stylesheet></action>
30
  </reference>
31
  </catalog_category_layered>
32
  <catalogsearch_result_index> <!-- find all catalog search result page -->
33
  <reference name="left"> <!-- find left column block -->
34
  <remove name="catalogsearch.leftnav"/> <!-- remove standard layered navigation -->
35
+ <remove name="enterprisesearch.leftnav"/> <!-- remove enterprise layered navigation -->
36
+ <block type="mana_filters/search" name="mana.catalogsearch.leftnav" before="-" template="catalog/layer/view.phtml"/>
37
+ </reference>
38
+ <reference name="head">
39
+ <action method="addCss"><stylesheet>css/mana_filters.css</stylesheet></action>
40
  </reference>
41
  </catalogsearch_result_index>
42
  </layout>
app/design/frontend/base/default/template/mana/core/js.phtml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* @var $this Mana_Core_Block_Js */
9
+ ?>
10
+ <script type="text/javascript">
11
+ //<![CDATA[
12
+ (function($) {
13
+ <?php if ($_translations = $this->getTranslations()) : ?>
14
+ $.__(<?php echo json_encode($_translations) ?>);
15
+ <?php endif; ?>
16
+ <?php if ($_options = $this->getOptions()) : ?>
17
+ $.options(<?php echo json_encode($_options) ?>);
18
+ <?php endif; ?>
19
+ })(jQuery);
20
+ //]]>
21
+ </script>
app/design/frontend/base/default/template/mana/core/popup.phtml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* @var $this Mage_Core_Block_Template */
9
+ ?>
10
+ <div id="m-popup" class="m-popup" style="display:none;">
11
+ </div>
12
+
app/design/frontend/base/default/template/mana/core/wait.phtml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Core
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /* @var $this Mage_Core_Block_Template */
9
+ ?>
10
+ <div id="m-wait" style="display:none;">
11
+ <p class="loader" id="loading_mask_loader"><img src="<?php echo $this->getSkinUrl('images/mana_core/m-wait.gif') ?>" alt="<?php echo $this->__('Loading...') ?>"/><br/><?php echo $this->__('Please wait...') ?></p>
12
+ </div>
13
+
app/design/frontend/base/default/template/mana/filters/items/list.phtml CHANGED
@@ -14,16 +14,15 @@
14
  */
15
  /* @var $this Mana_Filters_Block_Filter_Attribute */
16
  ?>
17
- <?php /* @var $_ext Mana_Filters_Helper_Extended */ $_ext = Mage::helper(strtolower('Mana_Filters/Extended')); ?>
18
- <ol>
19
  <?php foreach ($this->getItems() as $_item): ?>
20
- <li>
21
  <?php // MANA BEGIN ?>
22
  <?php if ($_item->getMSelected()): ?>
23
  <span class="m-selected-filter-item"><?php echo $_item->getLabel() ?></span>
24
  <?php else : ?>
25
  <?php if ($_item->getCount() > 0): ?>
26
- <a href="<?php echo $this->urlEscape($_item->getUrl()) ?>"><?php echo $_item->getLabel() ?></a>
27
  <?php else: echo $_item->getLabel() ?>
28
  <?php endif; ?>
29
  <?php endif; ?>
@@ -32,4 +31,4 @@
32
  </li>
33
  <?php endforeach ?>
34
  </ol>
35
- <?php echo $_ext->getNamedHtml('show_more', array('block' => $this)) ?>
14
  */
15
  /* @var $this Mana_Filters_Block_Filter_Attribute */
16
  ?>
17
+ <ol class="m-filter-item-list">
 
18
  <?php foreach ($this->getItems() as $_item): ?>
19
+ <li <?php if ($_item->getMSelected()): ?>class="m-selected-ln-item"<?php endif; ?>>
20
  <?php // MANA BEGIN ?>
21
  <?php if ($_item->getMSelected()): ?>
22
  <span class="m-selected-filter-item"><?php echo $_item->getLabel() ?></span>
23
  <?php else : ?>
24
  <?php if ($_item->getCount() > 0): ?>
25
+ <a href="<?php echo $this->urlEscape($_item->getUrl()) ?>" title="<?php echo $_item->getLabel() ?>"><?php echo $_item->getLabel() ?></a>
26
  <?php else: echo $_item->getLabel() ?>
27
  <?php endif; ?>
28
  <?php endif; ?>
31
  </li>
32
  <?php endforeach ?>
33
  </ol>
34
+ <?php echo Mage::helper('mana_core')->getNamedHtml('mana_filters/markup', 'after_items', array('block' => $this)) ?>
app/design/frontend/base/default/template/mana/filters/items/list_popup.phtml ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ */
11
+ /* @var $this Mana_Filters_Block_Filter_Attribute */
12
+ /* @var $filters Mana_Filters_Helper_Data */ $filters = Mage::helper(strtolower('Mana_Filters'));
13
+ /* @var $showmore ManaPro_FilterShowMore_Helper_Data */ $showmore = Mage::helper(strtolower('ManaPro_FilterShowMore'));
14
+ $MAX_ROW_COUNT = $showmore->getMaxRowCount(); // 20
15
+ $MAX_COLUMN_COUNT = $showmore->getMaxColumnCount(); // 4
16
+ $items = $this->getItems();
17
+ list($rowCount, $columnCount) = $showmore->getPopupDimensions($items, $MAX_ROW_COUNT, $MAX_COLUMN_COUNT);
18
+ ?>
19
+ <div class="m-filter-popup">
20
+ <ol class="m-rows" data-max-rows="<?php echo $MAX_ROW_COUNT ?>">
21
+ <?php for($rowIndex = 0; $rowIndex < $rowCount; $rowIndex++) : ?>
22
+ <li>
23
+ <ol class="m-columns">
24
+ <?php for ($columnIndex = 0; $columnIndex < $columnCount; $columnIndex++) : if ($columnIndex * $rowCount + $rowIndex < count($items)) : ?>
25
+ <?php $_item = $items[$columnIndex * $rowCount + $rowIndex]; ?>
26
+ <li <?php if ($_item->getMSelected()): ?>class="m-selected-ln-item"<?php endif; ?>>
27
+ <?php if ($_item->getCount() > 0): ?>
28
+ <a href="#" title="<?php echo $_item->getLabel() ?>"
29
+ onClick="jQuery(this).parent().toggleClass('m-selected-ln-item'); jQuery(this).children().toggleClass('m-selected-filter-item'); return jQuery.mShowMorePopupSelect('<?php echo $_item->getSeoValue() ?>', jQuery(this).parent().hasClass('m-selected-ln-item'));">
30
+ <span <?php if ($_item->getMSelected()): ?>class="m-selected-filter-item"<?php endif; ?> ><?php echo $_item->getLabel() ?></span>
31
+ </a>
32
+ <?php else: echo $_item->getLabel(); ?>
33
+ <?php endif; ?>
34
+ (<?php echo $_item->getCount() ?>)
35
+ </li>
36
+ <?php endif; endfor; ?>
37
+ </ol>
38
+ </li>
39
+ <?php endfor; ?>
40
+ </ol>
41
+ <div class="buttons-set">
42
+ <button type="button" title="<?php echo $this->__('Close') ?>" class="button"
43
+ onclick="return jQuery.mClosePopup();">
44
+ <span><span><?php echo $this->__('Close') ?></span></span></button>
45
+ <button type="button" title="<?php echo $this->__('Apply') ?>" class="button"
46
+ onclick="return jQuery.mShowMorePopupApply();">
47
+ <span><span><?php echo $this->__('Apply') ?></span></span></button>
48
+ </div>
49
+ </div>
app/design/frontend/base/default/template/mana/filters/items/standard.phtml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * Template for showing options for filter as a HTML list
10
+ * @author Mana Team
11
+ * Injected instead of standard catalog/layer/filter.phtml in Mana_Filters_Block_Filter_Attribute constructor.
12
+ * This template is overridden by copying (template body was pasted from catalog/layer/filter.phtml
13
+ * and modified as needed). All changes are marked with comments.
14
+ */
15
+ /* @var $this Mana_Filters_Block_Filter_Attribute */
16
+ ?>
17
+ <ol class="m-filter-item-list">
18
+ <?php foreach ($this->getItems() as $_item): ?>
19
+ <li <?php if ($_item->getMSelected()): ?>class="m-selected-ln-item"<?php endif; ?>>
20
+ <?php // MANA BEGIN ?>
21
+ <?php if ($_item->getMSelected()): ?>
22
+ <span class="m-selected-filter-item"><?php echo $_item->getLabel() ?></span>
23
+ <?php else : ?>
24
+ <?php if ($_item->getCount() > 0): ?>
25
+ <a href="<?php echo $this->urlEscape($_item->getReplaceUrl()) ?>" title="<?php echo $_item->getLabel() ?>"><?php echo $_item->getLabel() ?></a>
26
+ <?php else: echo $_item->getLabel() ?>
27
+ <?php endif; ?>
28
+ <?php endif; ?>
29
+ <?php // MANA END ?>
30
+ (<?php echo $_item->getCount() ?>)
31
+ </li>
32
+ <?php endforeach ?>
33
+ </ol>
34
+ <?php echo Mage::helper('mana_core')->getNamedHtml('mana_filters/markup', 'after_items', array('block' => $this)) ?>
app/design/frontend/base/default/template/mana/filters/items/standard_popup.phtml ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ /**
9
+ * @author Mana Team
10
+ */
11
+ /* @var $this Mana_Filters_Block_Filter_Attribute */
12
+ /* @var $filters Mana_Filters_Helper_Data */ $filters = Mage::helper(strtolower('Mana_Filters'));
13
+ /* @var $showmore ManaPro_FilterShowMore_Helper_Data */ $showmore = Mage::helper(strtolower('ManaPro_FilterShowMore'));
14
+ $MAX_ROW_COUNT = $showmore->getMaxRowCount(); // 20
15
+ $MAX_COLUMN_COUNT = $showmore->getMaxColumnCount(); // 4
16
+ $items = $this->getItems();
17
+ list($rowCount, $columnCount) = $showmore->getPopupDimensions($items, $MAX_ROW_COUNT, $MAX_COLUMN_COUNT);
18
+ ?>
19
+ <div class="m-filter-popup">
20
+ <ol class="m-rows" data-max-rows="<?php echo $MAX_ROW_COUNT ?>">
21
+ <?php for($rowIndex = 0; $rowIndex < $rowCount; $rowIndex++) : ?>
22
+ <li>
23
+ <ol class="m-columns">
24
+ <?php for ($columnIndex = 0; $columnIndex < $columnCount; $columnIndex++) : if ($columnIndex * $rowCount + $rowIndex < count($items)) : ?>
25
+ <?php $_item = $items[$columnIndex * $rowCount + $rowIndex]; ?>
26
+ <li <?php if ($_item->getMSelected()): ?>class="m-selected-ln-item"<?php endif; ?>>
27
+ <?php if ($_item->getMSelected()): ?>
28
+ <span class="m-selected-filter-item"><?php echo $_item->getLabel() ?></span>
29
+ <?php else : ?>
30
+ <?php if ($_item->getCount() > 0): ?>
31
+ <a href="#" onClick="return jQuery.mShowMorePopupApply('<?php echo $_item->getSeoValue() ?>');"
32
+ title="<?php echo $_item->getLabel() ?>"><?php echo $_item->getLabel() ?></a>
33
+ <?php else: echo $_item->getLabel() ?>
34
+ <?php endif; ?>
35
+ <?php endif; ?>
36
+ (<?php echo $_item->getCount() ?>)
37
+ </li>
38
+ <?php endif; endfor; ?>
39
+ </ol>
40
+ </li>
41
+ <?php endfor; ?>
42
+ </ol>
43
+ </div>
app/design/frontend/base/default/template/mana/filters/state.phtml ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Mana
4
+ * @package Mana_Filters
5
+ * @copyright Copyright (c) http://www.manadev.com
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ ?>
9
+ <?php
10
+ /**
11
+ * Category layered navigation state
12
+ *
13
+ * @see Mage_Catalog_Block_Layer_State
14
+ */
15
+ ?>
16
+ <?php $_filters = $this->getActiveFilters() ?>
17
+ <?php if(!empty($_filters)): ?>
18
+ <div class="currently">
19
+ <p class="block-subtitle"><?php echo $this->__('Currently Shopping by:') ?></p>
20
+ <ol>
21
+ <?php foreach ($_filters as $_filter): ?>
22
+ <?php if ($_html = $this->getValueHtml($_filter)) : ?>
23
+ <?php echo $_html ?>
24
+ <?php else : ?>
25
+ <li>
26
+ <a href="<?php echo $_filter->getRemoveUrl() ?>" title="<?php echo $this->__('Remove This Item') ?>" class="btn-remove"><?php echo $this->__('Remove This Item') ?></a>
27
+ <span class="label"><?php echo $this->__($_filter->getName()) ?>:</span> <?php echo $this->stripTags($_filter->getLabel()) ?>
28
+ </li>
29
+ <?php endif; ?>
30
+ <?php endforeach; ?>
31
+ </ol>
32
+ <?php if (!Mage::helper('mana_core')->isMageVersionEqualOrGreater('1.7')) : ?>
33
+ <div class="actions"><a href="<?php echo $this->getClearUrl() ?>"><?php echo $this->__('Clear All') ?></a></div>
34
+ <?php endif; ?>
35
+ </div>
36
+ <?php endif; ?>
app/etc/modules/Mana_Core.xml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ @category Mana
4
+ @package Mana_Core
5
+ @copyright Copyright (c) http://www.manadev.com
6
+ @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ -->
8
+ <!-- BASED ON SNIPPET: New Module/etc/modules/<module>.xml -->
9
+ <config>
10
+ <!-- This section activates module in Magento system. -->
11
+ <modules>
12
+ <Mana_Core>
13
+ <!-- This actually an activation instruction. You can set it to false to temporarily deactivate module (
14
+ in this case Magento will behave as if module does not exist). -->
15
+ <active>true</active>
16
+ <!-- This instructs Magento to search for module code in app/code/local directory. -->
17
+ <codePool>local</codePool>
18
+ <!-- INSERT HERE: depends -->
19
+ </Mana_Core>
20
+ </modules>
21
+ </config>
app/etc/modules/Mana_Db.xml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ @category Mana
4
+ @package Mana_Db
5
+ @copyright Copyright (c) http://www.manadev.com
6
+ @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ -->
8
+ <!-- BASED ON SNIPPET: New Module/etc/modules/<module>.xml -->
9
+ <config>
10
+ <!-- This section activates module in Magento system. -->
11
+ <modules>
12
+ <Mana_Db>
13
+ <!-- This actually an activation instruction. You can set it to false to temporarily deactivate module (
14
+ in this case Magento will behave as if module does not exist). -->
15
+ <active>true</active><!-- This module is on hold, without any stable version -->
16
+ <!-- This instructs Magento to search for module code in app/code/local directory. -->
17
+ <codePool>local</codePool>
18
+ <depends>
19
+ <Mana_Core />
20
+ </depends>
21
+ </Mana_Db>
22
+ </modules>
23
+ </config>
app/etc/modules/Mana_Filters.xml CHANGED
@@ -20,6 +20,8 @@
20
  <depends>
21
  <Mage_Catalog />
22
  <Mage_CatalogSearch />
 
 
23
  </depends>
24
  </Mana_Filters>
25
  </modules>
20
  <depends>
21
  <Mage_Catalog />
22
  <Mage_CatalogSearch />
23
+ <Mana_Core />
24
+ <Mana_Db />
25
  </depends>
26
  </Mana_Filters>
27
  </modules>
app/locale/en_US/Mana_Core.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Use System Configuration","Use System Configuration"
2
+ "nothing","nothing"
3
+ "CDATA section (<![CDATA[ ]]>)","CDATA section (<![CDATA[ ]]>)"
4
+ "comment (<!-- -->)","comment (<!-- -->)"
5
+ "raw text","raw text"
6
+ "element or attribute name","element or attribute name"
7
+ "attribute value","attribute value"
8
+ "HTML parser error %s: %s expected%s","HTML parser error %s: %s expected%s"
9
+ "HTML parser error %s: closing tag for %s expected%s","HTML parser error %s: closing tag for %s expected%s"
10
+ "HTML parser error %s: %s or %s expected%s","HTML parser error %s: %s or %s expected%s"
11
+ "HTML parser error %s: unexpected %s%s","HTML parser error %s: unexpected %s%s"
12
+ "HTML read error %s: whitespace not expected%s","HTML read error %s: whitespace not expected%s"
13
+ "HTML read error %s: unexpected character%s","HTML read error %s: unexpected character%s"
14
+ "HTML read error %s: unexpected end of text%s","HTML read error %s: unexpected end of text%s"
15
+ "This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation.","This Magento installation contains pending database installation/upgrade scripts. Please turn off Magento compilation feature while installing/upgrading new modules in Admin Panel menu System->Tools->Compilation."
16
+ "Same For All Stores","Same For All Stores"
17
+ "Configure Mana Extensions","Configure Mana Extensions"
18
+ "Please wait...","Please wait..."
19
+ "Loading...","Loading..."
app/locale/en_US/Mana_Db.csv ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Replica model %s not found.","Replica model %s not found."
2
+ "Two attributes cannot use the same database column %s.%s","Two attributes cannot use the same database column %s.%s"
3
+ "Two attributes cannot define the same database key %s.%s","Two attributes cannot define the same database key %s.%s"
4
+ "Two attributes cannot define the same database constraint %s.%s","Two attributes cannot define the same database constraint %s.%s"
5
+ "Store-level entity %s name should end with _store","Store-level entity %s name should end with _store"
6
+ "Table for entity %s not defined","Table for entity %s not defined"
7
+ "Altering database columns currently not supported","Altering database columns currently not supported"
8
+ "Altering database contsraints currently not supported","Altering database contsraints currently not supported"
9
+ "Altering database keys currently not supported","Altering database keys currently not supported"
10
+ "%s/%s/%s: %s parameter is required","%s/%s/%s: %s parameter is required"
11
+ "Entities with circular dependencies found: %s","Entities with circular dependencies found: %s"
12
+ "Default Values","Default Values"
13
+ "Propagate default values throughout the system","Propagate default values throughout the system"
14
+ "Use Default","Use Default"
app/locale/en_US/Mana_Filters.csv CHANGED
@@ -1,2 +1,21 @@
1
  "Filters of type ""%s"" can not be displayed - no template installed.","Filters of type ""%s"" can not be displayed - no template installed."
2
- "List","List"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  "Filters of type ""%s"" can not be displayed - no template installed.","Filters of type ""%s"" can not be displayed - no template installed."
2
+ "Text (Multiple Select Enabled)","Text (Multiple Select Enabled)"
3
+ "Text (One Item Can Be Selected At A Time)","Text (One Item Can Be Selected At A Time)"
4
+ "Category","Category"
5
+ "Fields With Non-Default Values (Bit Mask)","Fields With Non-Default Values (Bit Mask)"
6
+ "Is Visible In Category","Is Visible In Category"
7
+ "Is Visible In Search","Is Visible In Search"
8
+ "Position","Position"
9
+ "All filters are shown in layered navigation ordered by position","All filters are shown in layered navigation ordered by position"
10
+ "Can Store Customer Select Multiple Values","Can Store Customer Select Multiple Values"
11
+ "Code","Code"
12
+ "Name","Name"
13
+ "Use Product Attribute","Use Product Attribute"
14
+ "Display Filter Items As","Display Filter Items As"
15
+ "Position (selected at the top)","Position (selected at the top)"
16
+ "Name","Name"
17
+ "Name (selected at the top)","Name (selected at the top)"
18
+ "Count","Count"
19
+ "Count (selected at the top)","Count (selected at the top)"
20
+ "Logical OR","Logical OR"
21
+ "Logical AND","Logical AND"
js/jquery/advListRotator.js ADDED
@@ -0,0 +1,591 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if (typeof Object.create !== 'function') {
2
+ Object.create = function (o) {
3
+ function F() {}
4
+ F.prototype = o;
5
+ return new F();
6
+ };
7
+ }
8
+ (function($){
9
+ $.fn.extend({
10
+ mAdvListRotator: function(options) {
11
+ if (this.length) {
12
+ return this.each(function() {
13
+ var obj = Object.create(MAdvancedListRotatorClass);
14
+ obj.init(this, options);
15
+ $.data(this, 'advListRotator', MAdvancedListRotatorClass);
16
+ });
17
+ }
18
+ return false;
19
+ }
20
+ });
21
+ })(jQuery);
22
+
23
+ /* Advanced List Rotator
24
+ * Copyright (c) 2010 Lars Boldt (larsboldt.com)
25
+ * E-mail: boldt.lars@gmail.com
26
+ * Version: 1.3
27
+ * Released: 07.15.2010
28
+ *
29
+ * Permission is hereby granted, free of charge, to any person
30
+ * obtaining a copy of this software and associated documentation
31
+ * files (the "Software"), to deal in the Software without
32
+ * restriction, including without limitation the rights to use,
33
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
34
+ * copies of the Software, and to permit persons to whom the
35
+ * Software is furnished to do so, subject to the following
36
+ * conditions:
37
+ *
38
+ * The above copyright notice and this permission notice shall be
39
+ * included in all copies or substantial portions of the Software.
40
+ *
41
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
42
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
43
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
44
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
45
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
46
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
47
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
48
+ * OTHER DEALINGS IN THE SOFTWARE.
49
+ */
50
+ var MAdvancedListRotatorClass = {
51
+ init: function(element, options) {
52
+ // Store "this" for reference
53
+ var c = this;
54
+ // Settings
55
+ c.settings = jQuery.extend({
56
+ rotationInterval: 5000,
57
+ hideEffectTimer: 1000,
58
+ hideEffect: false,
59
+ hideEffectOptions: {},
60
+ hideRandomEffect:false,
61
+ hideRandomEffects:new Array('blind', 'clip', 'explode', 'fade', 'fold'),
62
+ showEffectTimer:1000,
63
+ showEffect:false,
64
+ showEffectOptions:{},
65
+ showRandomEffect:false,
66
+ showRandomEffects:new Array('blind', 'clip', 'explode', 'fade', 'fold'),
67
+ showWhileHiding: false,
68
+ showWhileHidingDelayInterval: 0,
69
+ itemControl: {},
70
+ shuffle: false,
71
+ randomStart: false,
72
+ autoStart: true,
73
+ disableRotationEngine: false,
74
+ helper: false,
75
+ activeItemClass: 'alrActiveItem',
76
+ helperActiveItemClass: 'alrHelperActiveItem',
77
+ helperInteraction: 'mouseover',
78
+ startIndex: 0,
79
+ nextItemElement: false,
80
+ nextItemElementInteraction: 'click',
81
+ previousItemElement: false,
82
+ previousItemElementInteraction: 'click',
83
+ debug: false,
84
+ debugLevel: new Array('debug', 'info', 'warn', 'error')
85
+ }, c.settings, options);
86
+ // Effect options
87
+ if (c.settings.hideEffect == 'slide') {
88
+ c.settings.showEffect = c.settings.hideEffect;
89
+ c.settings.showEffectOptions = c.settings.hideEffectOptions;
90
+ }
91
+
92
+ c.settings.rotationInterval = parseInt(c.settings.rotationInterval);
93
+ c.settings.hideEffectTimer = parseInt(c.settings.hideEffectTimer);
94
+ c.settings.showEffectTimer = parseInt(c.settings.showEffectTimer);
95
+ c.settings.showWhileHidingDelayInterval = parseInt(c.settings.showWhileHidingDelayInterval);
96
+
97
+ c.hideEffectOptions = jQuery.extend({}, c.hideEffectOptions, c.settings.hideEffectOptions);
98
+ c.showEffectOptions = jQuery.extend({}, c.showEffectOptions, c.settings.showEffectOptions);
99
+
100
+ // Effect control
101
+ c.itemControl = jQuery.extend({}, c.itemControl, c.settings.itemControl);
102
+
103
+ // Store element for reference, both normal and jQuery
104
+ c.listRotator = element;
105
+ c.$listRotator = jQuery(element);
106
+ // Total items (li's in ul)
107
+ c.totalItems = c.$listRotator.children().length; // manadev
108
+ // Current item
109
+ c.currentItem = (c.settings.startIndex >= 0 && c.settings.startIndex < c.totalItems) ? c.settings.startIndex : 0;
110
+ // Current effect
111
+ c.currentHideEffect = false;
112
+ c.currentShowEffect = false;
113
+ // Shuffle array
114
+ c.shuffledItems = new Array();
115
+ // Timer
116
+ c.tId = null;
117
+ // User interaction
118
+ c.userInteraction = false;
119
+ // Calculate next item index (we use this flag to skip calculating next item index when we move backwards)
120
+ c.calculateNextItem = true;
121
+ // Is animation running
122
+ c.animationRunning = false;
123
+ // Random start
124
+ if (c.settings.randomStart) {
125
+ c.currentItem = c.random(c.totalItems);
126
+ }
127
+ // Add currentItem to shuffle list to avoid showing item twice the first round
128
+ c.shuffledItems.push(c.currentItem);
129
+ // previousItem prevents shuffle ending and starting on the same item
130
+ c.previousItem = c.currentItem;
131
+
132
+ // Debug?
133
+ if (c.settings.debug && c.in_array('debug', c.settings.debugLevel)) {
134
+ console.debug('init: totalItems: ' + c.totalItems);
135
+ console.debug('init: startIndex: ' + c.currentItem);
136
+ if (c.settings.shuffle) {
137
+ console.debug('init: Pushed ' + c.currentItem + ' into shufflelist');
138
+ }
139
+ }
140
+
141
+ // Display first content
142
+ c.$listRotator.children().each(function(index) {
143
+ if (index == c.currentItem) {
144
+ if (c.settings.hideEffect == 'slide') {
145
+ var pos = '-' + c.currentItem*c.hideEffectOptions.slideBy + 'px';
146
+ c.$listRotator.css('left', pos);
147
+ }
148
+ jQuery(this).show();
149
+ jQuery(this).addClass(c.settings.activeItemClass);
150
+ c.setHelperClass(c, index, true);
151
+ }
152
+ });
153
+
154
+ // Loop all helper elements within the ul and bind mouseover/mouseout functionality to them
155
+ if (jQuery(c.settings.helper).length > 0) {
156
+ jQuery(c.settings.helper).children().each(function() {
157
+ jQuery(this).bind(c.settings.helperInteraction, function() {
158
+ // Set userInteraction true
159
+ c.userInteraction = true;
160
+ // Stop rotationEngine
161
+ c.stopRotationEngine(c);
162
+ // Make sure currentEffect is actually set (certain configs might not set this value)
163
+ c.currentEffect = c.getEffect(c);
164
+
165
+ // Hide active content
166
+ if (c.currentEffect.hide != 'slide') {
167
+ c.$listRotator.children().hide();
168
+ }
169
+ // Remove active class from helper
170
+ c.$listRotator.children().removeClass(c.settings.activeItemClass);
171
+ jQuery(c.settings.helper).children().removeClass(c.settings.helperActiveItemClass);
172
+
173
+
174
+ var oldSelector = null;
175
+ c.$listRotator.children().each(function (index) {
176
+ if (index == c.currentItem) {
177
+ oldSelector = jQuery(this);
178
+ }
179
+ });
180
+ c.currentItem = jQuery(this).index();
181
+
182
+ // Show current content
183
+ c.$listRotator.children().each(function (index) {
184
+ if (index == c.currentItem) {
185
+ // Stop animations on this element
186
+ if (oldSelector && oldSelector.length) {
187
+ oldSelector.stop();
188
+ }
189
+ jQuery(this).stop();
190
+ c.$listRotator.stop();
191
+ c.runEffect(c, oldSelector, jQuery(this), true);
192
+
193
+ // Reset opacity incase animation was fade
194
+
195
+ // if (oldSelector && oldSelector.length) {
196
+ // oldSelector.fadeOut(200);
197
+ // }
198
+ // jQuery(this).fadeOut(0).show().fadeIn(200, function() {
199
+ // if (c.currentEffect.hide == 'slide') {
200
+ // c.runEffect(c, oldSelector, jQuery(this), false);
201
+ // }
202
+ //
203
+ // // Add active class to helper
204
+ // jQuery(this).addClass(c.settings.activeItemClass);
205
+ // c.setHelperClass(c, index, true);
206
+ // });
207
+
208
+ return false;
209
+ }
210
+ return true;
211
+ });
212
+ });
213
+ jQuery(this).bind('mouseout', function() {
214
+ // Start rotationEngine
215
+ c.startRotationEngine(c);
216
+ });
217
+ });
218
+ }
219
+
220
+ // Bind functionality to nextItemElement
221
+ if (jQuery(c.settings.nextItemElement).length > 0) {
222
+ jQuery(c.settings.nextItemElement).bind(c.settings.nextItemElementInteraction, function() {
223
+ c.moveToNextItem(c);
224
+ });
225
+ }
226
+
227
+ // Bind functionality to previousItemElement
228
+ if (jQuery(c.settings.previousItemElement).length > 0) {
229
+ jQuery(c.settings.previousItemElement).bind(c.settings.previousItemElementInteraction, function() {
230
+ c.moveToPreviousItem(c);
231
+ });
232
+ }
233
+
234
+ // Start rotationEngine if autoStart is true
235
+ if (c.settings.autoStart) {
236
+ c.startRotationEngine(c);
237
+ }
238
+ },
239
+
240
+ // rotationEngine
241
+ rotationEngine: function(c) {
242
+ // Stop rotationEngine
243
+ c.stopRotationEngine(c);
244
+
245
+ // Reset userInteraction
246
+ c.userInteraction = false;
247
+
248
+ // Hide open content
249
+ c.$listRotator.children().each(function(index) {
250
+ if (index == c.currentItem) {
251
+ // Should we calculate next item index?
252
+ if (c.calculateNextItem) {
253
+ // Is shuffle set?
254
+ if (c.settings.shuffle) {
255
+ // Get a random item, but make sure every item is shown in each rotation
256
+ c.currentItem = c.shuffleRotationEngine(c, c.totalItems);
257
+ } else {
258
+ // Activate next item
259
+ c.currentItem++;
260
+ // Make sure currentItem resets at the end of the itemlist
261
+ c.currentItem = (c.currentItem >= c.totalItems) ? 0 : c.currentItem;
262
+ }
263
+ } else {
264
+ // Calculate previous item index instead
265
+ // Going backwards is alittle more complicated because of shuffle
266
+ // Is shuffle enabled?
267
+ if (c.settings.shuffle) {
268
+ // Now we need to find the previous previous item based on the shuffleItems list
269
+ if (c.shuffledItems.length >= 1) {
270
+ for (var j=0; j < c.shuffledItems.length; j++) {
271
+ if (c.shuffledItems[j] == c.previousItem) {
272
+ // Moving backwards in the shuffledItems list will only hold true until we reach zero.
273
+ // At this point you will get a random item if you keep going backwards
274
+ // However, this is complicated by the fact that shuffledItems is reset at the last item.
275
+ // Moving backwards when you are at the last item in the current shuffle gives you a
276
+ // random item
277
+ do {
278
+ j--;
279
+ c.previousItem = (j < 0) ? c.shuffleRotationEngine(c, c.totalItems) : c.shuffledItems[j];
280
+ } while (c.currentItem == c.previousItem);
281
+ break;
282
+ }
283
+ }
284
+ } else {
285
+ // shuffledItems was reset.
286
+ c.previousItem = c.shuffleRotationEngine(c, c.totalItems);
287
+ }
288
+ // Set currentItem to the previous shuffled item
289
+ c.currentItem = c.previousItem;
290
+ } else {
291
+ // Move index back once
292
+ c.currentItem--;
293
+ // Once index goes below zero we simply move it to the last item in the list, creating an endless loop
294
+ c.currentItem = (c.currentItem < 0) ? (c.totalItems-1) : c.currentItem;
295
+ }
296
+ }
297
+
298
+ // Loop all li elements until you locate the new active item
299
+ var newActiveItem = null;
300
+ c.$listRotator.children().each(function (idx, el) {
301
+ // Is next item found?
302
+ if (idx == c.currentItem) {
303
+ // Show item
304
+ newActiveItem = el;
305
+ // Give it the active class
306
+ c.setHelperClass(c, idx, true);
307
+ // Remove active class from current active item
308
+ c.setHelperClass(c, index, false);
309
+
310
+ return false;
311
+ }
312
+ return true;
313
+ });
314
+
315
+ // Reset item index calculation
316
+ c.calculateNextItem = true;
317
+
318
+ // Run effect
319
+ c.runEffect(c, jQuery(this), jQuery(newActiveItem), true);
320
+
321
+ return false;
322
+ }
323
+ return true;
324
+ });
325
+ },
326
+
327
+ continueRotation: function(c, oldSelector, newSelector, h) {
328
+ // Check if user interacted with helper during effect duration
329
+ if (! c.userInteraction) {
330
+ // Make sure the element is hidden when effect is done, not all effects end with hide
331
+ if (h) {
332
+ oldSelector.hide();
333
+ }
334
+ // Remove active class on previous active item
335
+ oldSelector.removeClass(c.settings.activeItemClass);
336
+
337
+ // Loop all li elements until you locate the new active item
338
+ if (newSelector.length) {
339
+ newSelector.addClass(c.settings.activeItemClass);
340
+ }
341
+
342
+ // Start rotationEngine
343
+ this.startRotationEngine(c);
344
+ }
345
+ },
346
+ continueShowWhileHiding:function (c, oldSelector, newSelector, runCallback) {
347
+ if (c.settings.showWhileHiding) {
348
+ c.showWhileHidingTimerId = setInterval(function () {
349
+ if (c.showWhileHidingTimerId) {
350
+ clearInterval(c.showWhileHidingTimerId);
351
+ c.showWhileHidingTimerId = null;
352
+ c.continueShow(c, oldSelector, newSelector, runCallback);
353
+ }
354
+ }, c.settings.showWhileHidingDelayInterval);
355
+ }
356
+ },
357
+ continueShow:function (c, oldSelector, newSelector, runCallback) {
358
+ if (c.currentEffect.show == 'fade') {
359
+ newSelector.fadeIn(c.settings.showEffectTimer, function () {
360
+ c.afterShow(c, oldSelector, newSelector, runCallback);
361
+ });
362
+ }
363
+ else if (c.currentEffect.show && oldSelector.effect) {
364
+ newSelector.show(c.currentEffect.show, c.settings.showEffectOptions, c.settings.showEffectTimer, function () {
365
+ c.afterShow(c, oldSelector, newSelector, runCallback);
366
+ });
367
+ }
368
+ else {
369
+ newSelector.show();
370
+ c.afterShow(c, oldSelector, newSelector, runCallback);
371
+ }
372
+ },
373
+ afterHide:function (c, oldSelector, newSelector, runCallback) {
374
+ c.hideRunning = false;
375
+ if (c.settings.showWhileHiding) {
376
+ if (!c.showRunning) {
377
+ c.animationRunning = false;
378
+ if (runCallback) {
379
+ c.continueRotation(c, oldSelector, newSelector, false);
380
+ }
381
+ }
382
+ }
383
+ else {
384
+ c.continueShow(c, oldSelector, newSelector, runCallback);
385
+ }
386
+ },
387
+ afterShow:function (c, oldSelector, newSelector, runCallback) {
388
+ c.showRunning = false;
389
+ if (!c.hideRunning) {
390
+ c.animationRunning = false;
391
+ if (runCallback) {
392
+ c.continueRotation(c, oldSelector, newSelector, false);
393
+ }
394
+ }
395
+ },
396
+
397
+ runEffect: function(c, oldSelector, newSelector, runCallback) {
398
+ // Set animationRunning flag to true
399
+ c.animationRunning = true;
400
+ // Make sure jQuery UI is installed before running UI effects, if fade effect or UI isn't installed, run the standard fadeOut effect
401
+ c.currentEffect = c.getEffect(c);
402
+ if (c.currentEffect.hide == 'slide') {
403
+ var pos = '-' + c.currentItem*c.hideEffectOptions.slideBy;
404
+ newSelector.show();
405
+ if (runCallback) {
406
+ c.$listRotator.animate({left: pos}, c.settings.hideEffectTimer, 'swing', function() {
407
+ c.animationRunning = false;
408
+ if (runCallback) {
409
+ c.continueRotation(c, oldSelector, newSelector, false);
410
+ }
411
+ });
412
+ }
413
+ else {
414
+ c.$listRotator.css({left:pos + "px"});
415
+ }
416
+ }
417
+ else {
418
+ c.hideRunning = true;
419
+ c.showRunning = true;
420
+ if (c.currentEffect.hide == 'fade') {
421
+ oldSelector.fadeOut(c.settings.hideEffectTimer, function () {
422
+ c.afterHide(c, oldSelector, newSelector, runCallback);
423
+ });
424
+ c.continueShowWhileHiding(c, oldSelector, newSelector, runCallback);
425
+ }
426
+ else if (c.currentEffect.hide && oldSelector.effect) {
427
+ oldSelector.hide(c.currentEffect.hide, c.settings.hideEffectOptions, c.settings.hideEffectTimer, function() {
428
+ c.afterHide(c, oldSelector, newSelector, runCallback);
429
+ });
430
+ c.continueShowWhileHiding(c, oldSelector, newSelector, runCallback);
431
+ }
432
+ else {
433
+ oldSelector.hide();
434
+ c.afterHide(c, oldSelector, newSelector, runCallback);
435
+ }
436
+ }
437
+ },
438
+
439
+ stopRotationEngine: function(c) {
440
+ // Stop rotationEngine
441
+ if (c.tId) {
442
+ clearInterval(c.tId);
443
+ c.tId = null;
444
+ // Debug?
445
+ if (c.settings.debug && c.in_array('info', c.settings.debugLevel)) {
446
+ console.info('stopRotationEngine: Rotation Engine stopped');
447
+ }
448
+ }
449
+ },
450
+
451
+ startRotationEngine: function(c) {
452
+ // Stop rotationEngine
453
+ c.stopRotationEngine(c);
454
+ // Start rotationEngine as long as disableRotationEngine is false
455
+ if (! c.settings.disableRotationEngine) {
456
+ var rInt = c.settings.rotationInterval;
457
+ c.tId = setInterval(function() {c.rotationEngine(c)}, rInt);
458
+ // Debug?
459
+ if (c.settings.debug && c.in_array('info', c.settings.debugLevel)) {
460
+ console.info('startRotationEngine: Rotation Engine started; interval ' + rInt + '; currentItem ' + c.currentItem);
461
+ }
462
+ }
463
+ },
464
+
465
+ shuffleRotationEngine: function(c, max) {
466
+ // Number holds the random generated number
467
+ var number = c.random(max);
468
+ // Loop until we get a random number that has not been used in this rotation or was the last number of the previous rotation
469
+ while (c.in_array(number, c.shuffledItems) || number == c.previousItem) {
470
+ // Generate a new number until we get the one we want
471
+ number = c.random(max);
472
+ }
473
+ // Set generated number to previousItem
474
+ c.previousItem = number;
475
+ // Add generated number to shuffle list to avoid showing it twice or more each rotation
476
+ c.shuffledItems.push(number);
477
+ // Reset shuffle list if we've been through all the items
478
+ if (c.shuffledItems.length == c.totalItems) {
479
+ c.shuffledItems = new Array();
480
+ }
481
+ // Debug?
482
+ if (c.settings.debug && c.in_array('debug', c.settings.debugLevel)) {
483
+ console.debug('shuffleRotationEngine: Number ' + number + ' generated');
484
+ if (c.shuffledItems.length <= 0) {
485
+ console.debug('shuffleRotationEngine: Shufflelist was reset');
486
+ }
487
+ }
488
+ // Return generated number
489
+ return number;
490
+ },
491
+
492
+ in_array: function(needle, haystack) {
493
+ // Returns true if needle is in haystack
494
+ for (n in haystack) {
495
+ if (haystack[n] == needle) {
496
+ return true;
497
+ }
498
+ }
499
+ return false;
500
+ },
501
+
502
+ random: function(max) {
503
+ // Return a number between 0 and "max"
504
+ return Math.floor(Math.random()*max);
505
+ },
506
+
507
+ setHelperClass: function(c, n, status) {
508
+ // Make sure helper object is set
509
+ if (jQuery(c.settings.helper).length > 0) {
510
+ // Loop childs of helper object
511
+ jQuery(c.settings.helper).children().each(function(index) {
512
+ // Find the correct child
513
+ if (index == n) {
514
+ // Toggle class
515
+ if (status) {
516
+ jQuery(this).addClass(c.settings.helperActiveItemClass);
517
+ } else {
518
+ jQuery(this).removeClass(c.settings.helperActiveItemClass);
519
+ }
520
+ return false;
521
+ }
522
+ return true;
523
+ });
524
+ }
525
+ return false;
526
+ },
527
+
528
+ interact: function(c) {
529
+ // Set userInteraction true
530
+ c.userInteraction = true;
531
+ // Stop rotationEngine
532
+ c.stopRotationEngine(c);
533
+ // Make sure currentEffect is actually set (certain configs might not set this value)
534
+ c.currentEffect = c.getEffect(c);
535
+
536
+ var oldSelector = null;
537
+ c.$listRotator.children().each(function (index) {
538
+ if (index == c.currentItem) {
539
+ oldSelector = jQuery(this);
540
+ }
541
+ });
542
+
543
+ if (oldSelector && oldSelector.length) {
544
+ oldSelector.stop(true, true);
545
+ }
546
+ c.$listRotator.stop(true, true);
547
+ // if (c.currentEffect.hide != 'slide') {
548
+ // oldSelector.hide();
549
+ // }
550
+ },
551
+ moveToNextItem: function(c) {
552
+ // Move can only be done when animation isn't running
553
+ // if (! c.animationRunning) {
554
+ // Make sure we intercept any ongoing animations with the userInteraction flag
555
+ // c.userInteraction = true;
556
+ // Call the next item, rotationEngine will automatically figure out which item is next
557
+ c.interact(c);
558
+ c.rotationEngine(c);
559
+ // }
560
+ },
561
+
562
+ moveToPreviousItem: function(c) {
563
+ // Move can only be done when animation isn't running
564
+ // if (! c.animationRunning) {
565
+ // Make sure we intercept any ongoing animations with the userInteraction flag
566
+ // c.userInteraction = true;
567
+ // Setting calculate next item to false will tell rotationEngine to calculate the previous item instead
568
+ c.interact(c);
569
+ c.calculateNextItem = false;
570
+ c.rotationEngine(c);
571
+ // }
572
+ },
573
+
574
+ // getItemObj: function(c) {
575
+ // //var actualItem = (c.currentItem > 0) ? c.currentItem-1 : c.totalItems-1;
576
+ // var obj = c.itemControl["listIndex_" + c.currentItem];
577
+ // return (typeof(obj) == 'undefined') ? false : obj;
578
+ // },
579
+
580
+ getEffect: function(c) {
581
+ var result = {
582
+ hide: c.settings.hideRandomEffect
583
+ ? c.settings.hideRandomEffects[c.random(c.settings.hideRandomEffects.length)]
584
+ : c.settings.hideEffect,
585
+ show:c.settings.showRandomEffect
586
+ ? c.settings.showRandomEffects[c.random(c.settings.showRandomEffects.length)]
587
+ : c.settings.showEffect
588
+ };
589
+ return result;
590
+ }
591
+ }
js/jquery/fileuploader.js ADDED
@@ -0,0 +1,1257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * http://github.com/valums/file-uploader
3
+ *
4
+ * Multiple file upload component with progress-bar, drag-and-drop.
5
+ * © 2010 Andrew Valums ( andrew(at)valums.com )
6
+ *
7
+ * Licensed under GNU GPL 2 or later, see license.txt.
8
+ */
9
+
10
+ //
11
+ // Helper functions
12
+ //
13
+
14
+ var qq = qq || {};
15
+
16
+ /**
17
+ * Adds all missing properties from second obj to first obj
18
+ */
19
+ qq.extend = function(first, second){
20
+ for (var prop in second){
21
+ first[prop] = second[prop];
22
+ }
23
+ };
24
+
25
+ /**
26
+ * Searches for a given element in the array, returns -1 if it is not present.
27
+ * @param {Number} [from] The index at which to begin the search
28
+ */
29
+ qq.indexOf = function(arr, elt, from){
30
+ if (arr.indexOf) return arr.indexOf(elt, from);
31
+
32
+ from = from || 0;
33
+ var len = arr.length;
34
+
35
+ if (from < 0) from += len;
36
+
37
+ for (; from < len; from++){
38
+ if (from in arr && arr[from] === elt){
39
+ return from;
40
+ }
41
+ }
42
+ return -1;
43
+ };
44
+
45
+ qq.getUniqueId = (function(){
46
+ var id = 0;
47
+ return function(){ return id++; };
48
+ })();
49
+
50
+ //
51
+ // Events
52
+
53
+ qq.attach = function(element, type, fn){
54
+ if (element.addEventListener){
55
+ element.addEventListener(type, fn, false);
56
+ } else if (element.attachEvent){
57
+ element.attachEvent('on' + type, fn);
58
+ }
59
+ };
60
+ qq.detach = function(element, type, fn){
61
+ if (element.removeEventListener){
62
+ element.removeEventListener(type, fn, false);
63
+ } else if (element.attachEvent){
64
+ element.detachEvent('on' + type, fn);
65
+ }
66
+ };
67
+
68
+ qq.preventDefault = function(e){
69
+ if (e.preventDefault){
70
+ e.preventDefault();
71
+ } else{
72
+ e.returnValue = false;
73
+ }
74
+ };
75
+
76
+ //
77
+ // Node manipulations
78
+
79
+ /**
80
+ * Insert node a before node b.
81
+ */
82
+ qq.insertBefore = function(a, b){
83
+ b.parentNode.insertBefore(a, b);
84
+ };
85
+ qq.remove = function(element){
86
+ element.parentNode.removeChild(element);
87
+ };
88
+
89
+ qq.contains = function(parent, descendant){
90
+ // compareposition returns false in this case
91
+ if (parent == descendant) return true;
92
+
93
+ if (parent.contains){
94
+ return parent.contains(descendant);
95
+ } else {
96
+ return !!(descendant.compareDocumentPosition(parent) & 8);
97
+ }
98
+ };
99
+
100
+ /**
101
+ * Creates and returns element from html string
102
+ * Uses innerHTML to create an element
103
+ */
104
+ qq.toElement = (function(){
105
+ var div = document.createElement('div');
106
+ return function(html){
107
+ div.innerHTML = html;
108
+ var element = div.firstChild;
109
+ div.removeChild(element);
110
+ return element;
111
+ };
112
+ })();
113
+
114
+ //
115
+ // Node properties and attributes
116
+
117
+ /**
118
+ * Sets styles for an element.
119
+ * Fixes opacity in IE6-8.
120
+ */
121
+ qq.css = function(element, styles){
122
+ if (styles.opacity != null){
123
+ if (typeof element.style.opacity != 'string' && typeof(element.filters) != 'undefined'){
124
+ styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')';
125
+ }
126
+ }
127
+ qq.extend(element.style, styles);
128
+ };
129
+ qq.hasClass = function(element, name){
130
+ var re = new RegExp('(^| )' + name + '( |$)');
131
+ return re.test(element.className);
132
+ };
133
+ qq.addClass = function(element, name){
134
+ if (!qq.hasClass(element, name)){
135
+ element.className += ' ' + name;
136
+ }
137
+ };
138
+ qq.removeClass = function(element, name){
139
+ var re = new RegExp('(^| )' + name + '( |$)');
140
+ element.className = element.className.replace(re, ' ').replace(/^\s+|\s+$/g, "");
141
+ };
142
+ qq.setText = function(element, text){
143
+ element.innerText = text;
144
+ element.textContent = text;
145
+ };
146
+
147
+ //
148
+ // Selecting elements
149
+
150
+ qq.children = function(element){
151
+ var children = [],
152
+ child = element.firstChild;
153
+
154
+ while (child){
155
+ if (child.nodeType == 1){
156
+ children.push(child);
157
+ }
158
+ child = child.nextSibling;
159
+ }
160
+
161
+ return children;
162
+ };
163
+
164
+ qq.getByClass = function(element, className){
165
+ if (element.querySelectorAll){
166
+ return element.querySelectorAll('.' + className);
167
+ }
168
+
169
+ var result = [];
170
+ var candidates = element.getElementsByTagName("*");
171
+ var len = candidates.length;
172
+
173
+ for (var i = 0; i < len; i++){
174
+ if (qq.hasClass(candidates[i], className)){
175
+ result.push(candidates[i]);
176
+ }
177
+ }
178
+ return result;
179
+ };
180
+
181
+ /**
182
+ * obj2url() takes a json-object as argument and generates
183
+ * a querystring. pretty much like jQuery.param()
184
+ *
185
+ * how to use:
186
+ *
187
+ * `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');`
188
+ *
189
+ * will result in:
190
+ *
191
+ * `http://any.url/upload?otherParam=value&a=b&c=d`
192
+ *
193
+ * @param Object JSON-Object
194
+ * @param String current querystring-part
195
+ * @return String encoded querystring
196
+ */
197
+ qq.obj2url = function(obj, temp, prefixDone){
198
+ var uristrings = [],
199
+ prefix = '&',
200
+ add = function(nextObj, i){
201
+ var nextTemp = temp
202
+ ? (/\[\]$/.test(temp)) // prevent double-encoding
203
+ ? temp
204
+ : temp+'['+i+']'
205
+ : i;
206
+ if ((nextTemp != 'undefined') && (i != 'undefined')) {
207
+ uristrings.push(
208
+ (typeof nextObj === 'object')
209
+ ? qq.obj2url(nextObj, nextTemp, true)
210
+ : (Object.prototype.toString.call(nextObj) === '[object Function]')
211
+ ? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj())
212
+ : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj)
213
+ );
214
+ }
215
+ };
216
+
217
+ if (!prefixDone && temp) {
218
+ prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?';
219
+ uristrings.push(temp);
220
+ uristrings.push(qq.obj2url(obj));
221
+ } else if ((Object.prototype.toString.call(obj) === '[object Array]') && (typeof obj != 'undefined') ) {
222
+ // we wont use a for-in-loop on an array (performance)
223
+ for (var i = 0, len = obj.length; i < len; ++i){
224
+ add(obj[i], i);
225
+ }
226
+ } else if ((typeof obj != 'undefined') && (obj !== null) && (typeof obj === "object")){
227
+ // for anything else but a scalar, we will use for-in-loop
228
+ for (var i in obj){
229
+ add(obj[i], i);
230
+ }
231
+ } else {
232
+ uristrings.push(encodeURIComponent(temp) + '=' + encodeURIComponent(obj));
233
+ }
234
+
235
+ return uristrings.join(prefix)
236
+ .replace(/^&/, '')
237
+ .replace(/%20/g, '+');
238
+ };
239
+
240
+ //
241
+ //
242
+ // Uploader Classes
243
+ //
244
+ //
245
+
246
+ var qq = qq || {};
247
+
248
+ /**
249
+ * Creates upload button, validates upload, but doesn't create file list or dd.
250
+ */
251
+ qq.FileUploaderBasic = function(o){
252
+ this._options = {
253
+ // set to true to see the server response
254
+ debug: false,
255
+ action: '/server/upload',
256
+ params: {},
257
+ button: null,
258
+ multiple: true,
259
+ maxConnections: 3,
260
+ // validation
261
+ allowedExtensions: [],
262
+ sizeLimit: 0,
263
+ minSizeLimit: 0,
264
+ // events
265
+ // return false to cancel submit
266
+ onSubmit: function(id, fileName){},
267
+ onProgress: function(id, fileName, loaded, total){},
268
+ onComplete: function(id, fileName, responseJSON){},
269
+ onCancel: function(id, fileName){},
270
+ // messages
271
+ messages: {
272
+ typeError: "{file} has invalid extension. Only {extensions} are allowed.",
273
+ sizeError: "{file} is too large, maximum file size is {sizeLimit}.",
274
+ minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.",
275
+ emptyError: "{file} is empty, please select files again without it.",
276
+ onLeave: "The files are being uploaded, if you leave now the upload will be cancelled."
277
+ },
278
+ showMessage: function(message){
279
+ alert(message);
280
+ }
281
+ };
282
+ qq.extend(this._options, o);
283
+
284
+ // number of files being uploaded
285
+ this._filesInProgress = 0;
286
+ this._handler = this._createUploadHandler();
287
+
288
+ if (this._options.button){
289
+ this._button = this._createUploadButton(this._options.button);
290
+ }
291
+
292
+ this._preventLeaveInProgress();
293
+ };
294
+
295
+ qq.FileUploaderBasic.prototype = {
296
+ setParams: function(params){
297
+ this._options.params = params;
298
+ },
299
+ getInProgress: function(){
300
+ return this._filesInProgress;
301
+ },
302
+ _createUploadButton: function(element){
303
+ var self = this;
304
+
305
+ return new qq.UploadButton({
306
+ element: element,
307
+ multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(),
308
+ onChange: function(input){
309
+ self._onInputChange(input);
310
+ }
311
+ });
312
+ },
313
+ _createUploadHandler: function(){
314
+ var self = this,
315
+ handlerClass;
316
+
317
+ if(qq.UploadHandlerXhr.isSupported()){
318
+ handlerClass = 'UploadHandlerXhr';
319
+ } else {
320
+ handlerClass = 'UploadHandlerForm';
321
+ }
322
+
323
+ var handler = new qq[handlerClass]({
324
+ debug: this._options.debug,
325
+ action: this._options.action,
326
+ maxConnections: this._options.maxConnections,
327
+ onProgress: function(id, fileName, loaded, total){
328
+ self._onProgress(id, fileName, loaded, total);
329
+ self._options.onProgress(id, fileName, loaded, total);
330
+ },
331
+ onComplete: function(id, fileName, result){
332
+ self._onComplete(id, fileName, result);
333
+ self._options.onComplete(id, fileName, result);
334
+ },
335
+ onCancel: function(id, fileName){
336
+ self._onCancel(id, fileName);
337
+ self._options.onCancel(id, fileName);
338
+ }
339
+ });
340
+
341
+ return handler;
342
+ },
343
+ _preventLeaveInProgress: function(){
344
+ var self = this;
345
+
346
+ qq.attach(window, 'beforeunload', function(e){
347
+ if (!self._filesInProgress){return;}
348
+
349
+ var e = e || window.event;
350
+ // for ie, ff
351
+ e.returnValue = self._options.messages.onLeave;
352
+ // for webkit
353
+ return self._options.messages.onLeave;
354
+ });
355
+ },
356
+ _onSubmit: function(id, fileName){
357
+ this._filesInProgress++;
358
+ },
359
+ _onProgress: function(id, fileName, loaded, total){
360
+ },
361
+ _onComplete: function(id, fileName, result){
362
+ this._filesInProgress--;
363
+ if (result.error){
364
+ this._options.showMessage(result.error);
365
+ }
366
+ },
367
+ _onCancel: function(id, fileName){
368
+ this._filesInProgress--;
369
+ },
370
+ _onInputChange: function(input){
371
+ if (this._handler instanceof qq.UploadHandlerXhr){
372
+ this._uploadFileList(input.files);
373
+ } else {
374
+ if (this._validateFile(input)){
375
+ this._uploadFile(input);
376
+ }
377
+ }
378
+ this._button.reset();
379
+ },
380
+ _uploadFileList: function(files){
381
+ for (var i=0; i<files.length; i++){
382
+ if ( !this._validateFile(files[i])){
383
+ return;
384
+ }
385
+ }
386
+
387
+ for (var i=0; i<files.length; i++){
388
+ this._uploadFile(files[i]);
389
+ }
390
+ },
391
+ _uploadFile: function(fileContainer){
392
+ var id = this._handler.add(fileContainer);
393
+ var fileName = this._handler.getName(id);
394
+
395
+ if (this._options.onSubmit(id, fileName) !== false){
396
+ this._onSubmit(id, fileName);
397
+ this._handler.upload(id, this._options.params);
398
+ }
399
+ },
400
+ _validateFile: function(file){
401
+ var name, size;
402
+
403
+ if (file.value){
404
+ // it is a file input
405
+ // get input value and remove path to normalize
406
+ name = file.value.replace(/.*(\/|\\)/, "");
407
+ } else {
408
+ // fix missing properties in Safari
409
+ name = file.fileName != null ? file.fileName : file.name;
410
+ size = file.fileSize != null ? file.fileSize : file.size;
411
+ }
412
+
413
+ if (! this._isAllowedExtension(name)){
414
+ this._error('typeError', name);
415
+ return false;
416
+
417
+ } else if (size === 0){
418
+ this._error('emptyError', name);
419
+ return false;
420
+
421
+ } else if (size && this._options.sizeLimit && size > this._options.sizeLimit){
422
+ this._error('sizeError', name);
423
+ return false;
424
+
425
+ } else if (size && size < this._options.minSizeLimit){
426
+ this._error('minSizeError', name);
427
+ return false;
428
+ }
429
+
430
+ return true;
431
+ },
432
+ _error: function(code, fileName){
433
+ var message = this._options.messages[code];
434
+ function r(name, replacement){ message = message.replace(name, replacement); }
435
+
436
+ r('{file}', this._formatFileName(fileName));
437
+ r('{extensions}', this._options.allowedExtensions.join(', '));
438
+ r('{sizeLimit}', this._formatSize(this._options.sizeLimit));
439
+ r('{minSizeLimit}', this._formatSize(this._options.minSizeLimit));
440
+
441
+ this._options.showMessage(message);
442
+ },
443
+ _formatFileName: function(name){
444
+ if (name.length > 33){
445
+ name = name.slice(0, 19) + '...' + name.slice(-13);
446
+ }
447
+ return name;
448
+ },
449
+ _isAllowedExtension: function(fileName){
450
+ var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : '';
451
+ var allowed = this._options.allowedExtensions;
452
+
453
+ if (!allowed.length){return true;}
454
+
455
+ for (var i=0; i<allowed.length; i++){
456
+ if (allowed[i].toLowerCase() == ext){ return true;}
457
+ }
458
+
459
+ return false;
460
+ },
461
+ _formatSize: function(bytes){
462
+ var i = -1;
463
+ do {
464
+ bytes = bytes / 1024;
465
+ i++;
466
+ } while (bytes > 99);
467
+
468
+ return Math.max(bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i];
469
+ }
470
+ };
471
+
472
+
473
+ /**
474
+ * Class that creates upload widget with drag-and-drop and file list
475
+ * @inherits qq.FileUploaderBasic
476
+ */
477
+ qq.FileUploader = function(o){
478
+ // call parent constructor
479
+ qq.FileUploaderBasic.apply(this, arguments);
480
+
481
+ // additional options
482
+ qq.extend(this._options, {
483
+ element: null,
484
+ // if set, will be used instead of qq-upload-list in template
485
+ listElement: null,
486
+
487
+ template: '<div class="qq-uploader">' +
488
+ '<div class="qq-upload-drop-area"><span>Drop files here to upload</span></div>' +
489
+ '<div class="qq-upload-button">Upload a file</div>' +
490
+ '<ul class="qq-upload-list"></ul>' +
491
+ '</div>',
492
+
493
+ // template for one item in file list
494
+ fileTemplate: '<li>' +
495
+ '<span class="qq-upload-file"></span>' +
496
+ '<span class="qq-upload-spinner"></span>' +
497
+ '<span class="qq-upload-size"></span>' +
498
+ '<a class="qq-upload-cancel" href="#">Cancel</a>' +
499
+ '<span class="qq-upload-failed-text">Failed</span>' +
500
+ '</li>',
501
+
502
+ classes: {
503
+ // used to get elements from templates
504
+ button: 'qq-upload-button',
505
+ drop: 'qq-upload-drop-area',
506
+ dropActive: 'qq-upload-drop-area-active',
507
+ list: 'qq-upload-list',
508
+
509
+ file: 'qq-upload-file',
510
+ spinner: 'qq-upload-spinner',
511
+ size: 'qq-upload-size',
512
+ cancel: 'qq-upload-cancel',
513
+
514
+ // added to list item when upload completes
515
+ // used in css to hide progress spinner
516
+ success: 'qq-upload-success',
517
+ fail: 'qq-upload-fail'
518
+ }
519
+ });
520
+ // overwrite options with user supplied
521
+ qq.extend(this._options, o);
522
+
523
+ this._element = this._options.element;
524
+ //this._element.innerHTML = this._options.template;
525
+ this._listElement = this._options.listElement;// || this._find(this._element, 'list');
526
+
527
+ this._classes = this._options.classes;
528
+
529
+ this._button = this._createUploadButton(this._element);//this._find(this._element, 'button'));
530
+
531
+ //this._bindCancelEvent();
532
+ //this._setupDragDrop();
533
+ };
534
+
535
+ // inherit from Basic Uploader
536
+ qq.extend(qq.FileUploader.prototype, qq.FileUploaderBasic.prototype);
537
+
538
+ qq.extend(qq.FileUploader.prototype, {
539
+ /**
540
+ * Gets one of the elements listed in this._options.classes
541
+ **/
542
+ _find: function(parent, type){
543
+ var element = qq.getByClass(parent, this._options.classes[type])[0];
544
+ if (!element){
545
+ throw new Error('element not found ' + type);
546
+ }
547
+
548
+ return element;
549
+ },
550
+ _setupDragDrop: function(){
551
+ var self = this,
552
+ dropArea = this._find(this._element, 'drop');
553
+
554
+ var dz = new qq.UploadDropZone({
555
+ element: dropArea,
556
+ onEnter: function(e){
557
+ qq.addClass(dropArea, self._classes.dropActive);
558
+ e.stopPropagation();
559
+ },
560
+ onLeave: function(e){
561
+ e.stopPropagation();
562
+ },
563
+ onLeaveNotDescendants: function(e){
564
+ qq.removeClass(dropArea, self._classes.dropActive);
565
+ },
566
+ onDrop: function(e){
567
+ dropArea.style.display = 'none';
568
+ qq.removeClass(dropArea, self._classes.dropActive);
569
+ self._uploadFileList(e.dataTransfer.files);
570
+ }
571
+ });
572
+
573
+ dropArea.style.display = 'none';
574
+
575
+ qq.attach(document, 'dragenter', function(e){
576
+ if (!dz._isValidFileDrag(e)) return;
577
+
578
+ dropArea.style.display = 'block';
579
+ });
580
+ qq.attach(document, 'dragleave', function(e){
581
+ if (!dz._isValidFileDrag(e)) return;
582
+
583
+ var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
584
+ // only fire when leaving document out
585
+ if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){
586
+ dropArea.style.display = 'none';
587
+ }
588
+ });
589
+ },
590
+ _onSubmit: function(id, fileName){
591
+ qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments);
592
+ this._addToList(id, fileName);
593
+
594
+ var overlay = jQuery('<div class="m-overlay"> </div>');
595
+ overlay.appendTo(document.body);
596
+ jQuery('#m-wait').show();
597
+ },
598
+ _onProgress: function(id, fileName, loaded, total){
599
+ qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments);
600
+
601
+ //var item = this._getItemByFileId(id);
602
+ /*var size = this._find(item, 'size');
603
+ size.style.display = 'inline';
604
+
605
+ var text;
606
+ if (loaded != total){
607
+ text = Math.round(loaded / total * 100) + '% from ' + this._formatSize(total);
608
+ } else {
609
+ text = this._formatSize(total);
610
+ }
611
+
612
+ qq.setText(size, text);*/
613
+ },
614
+ _onComplete: function(id, fileName, result){
615
+ qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments);
616
+
617
+ // mark completed
618
+ /*var item = this._getItemByFileId(id);
619
+ qq.remove(this._find(item, 'cancel'));
620
+ qq.remove(this._find(item, 'spinner'));
621
+
622
+ if (result.success){
623
+ qq.addClass(item, this._classes.success);
624
+ } else {
625
+ qq.addClass(item, this._classes.fail);
626
+ } */
627
+ jQuery('.m-overlay').remove();
628
+ jQuery('#m-wait').hide();
629
+ },
630
+ _addToList: function(id, fileName){
631
+ var item = qq.toElement(this._options.fileTemplate);
632
+ item.qqFileId = id;
633
+
634
+ var fileElement = this._find(item, 'file');
635
+ qq.setText(fileElement, this._formatFileName(fileName));
636
+ //this._find(item, 'size').style.display = 'none';
637
+
638
+ //this._listElement.appendChild(item);
639
+ },
640
+ _getItemByFileId: function(id){
641
+ var item = this._listElement.firstChild;
642
+
643
+ // there can't be txt nodes in dynamically created list
644
+ // and we can use nextSibling
645
+ while (item){
646
+ if (item.qqFileId == id) return item;
647
+ item = item.nextSibling;
648
+ }
649
+ },
650
+ /**
651
+ * delegate click event for cancel link
652
+ **/
653
+ _bindCancelEvent: function(){
654
+ var self = this,
655
+ list = this._listElement;
656
+
657
+ qq.attach(list, 'click', function(e){
658
+ e = e || window.event;
659
+ var target = e.target || e.srcElement;
660
+
661
+ if (qq.hasClass(target, self._classes.cancel)){
662
+ qq.preventDefault(e);
663
+
664
+ var item = target.parentNode;
665
+ self._handler.cancel(item.qqFileId);
666
+ qq.remove(item);
667
+ }
668
+ });
669
+ }
670
+ });
671
+
672
+ qq.UploadDropZone = function(o){
673
+ this._options = {
674
+ element: null,
675
+ onEnter: function(e){},
676
+ onLeave: function(e){},
677
+ // is not fired when leaving element by hovering descendants
678
+ onLeaveNotDescendants: function(e){},
679
+ onDrop: function(e){}
680
+ };
681
+ qq.extend(this._options, o);
682
+
683
+ this._element = this._options.element;
684
+
685
+ this._disableDropOutside();
686
+ this._attachEvents();
687
+ };
688
+
689
+ qq.UploadDropZone.prototype = {
690
+ _disableDropOutside: function(e){
691
+ // run only once for all instances
692
+ if (!qq.UploadDropZone.dropOutsideDisabled ){
693
+
694
+ qq.attach(document, 'dragover', function(e){
695
+ if (e.dataTransfer){
696
+ e.dataTransfer.dropEffect = 'none';
697
+ e.preventDefault();
698
+ }
699
+ });
700
+
701
+ qq.UploadDropZone.dropOutsideDisabled = true;
702
+ }
703
+ },
704
+ _attachEvents: function(){
705
+ var self = this;
706
+
707
+ qq.attach(self._element, 'dragover', function(e){
708
+ if (!self._isValidFileDrag(e)) return;
709
+
710
+ var effect = e.dataTransfer.effectAllowed;
711
+ if (effect == 'move' || effect == 'linkMove'){
712
+ e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)
713
+ } else {
714
+ e.dataTransfer.dropEffect = 'copy'; // for Chrome
715
+ }
716
+
717
+ e.stopPropagation();
718
+ e.preventDefault();
719
+ });
720
+
721
+ qq.attach(self._element, 'dragenter', function(e){
722
+ if (!self._isValidFileDrag(e)) return;
723
+
724
+ self._options.onEnter(e);
725
+ });
726
+
727
+ qq.attach(self._element, 'dragleave', function(e){
728
+ if (!self._isValidFileDrag(e)) return;
729
+
730
+ self._options.onLeave(e);
731
+
732
+ var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
733
+ // do not fire when moving a mouse over a descendant
734
+ if (qq.contains(this, relatedTarget)) return;
735
+
736
+ self._options.onLeaveNotDescendants(e);
737
+ });
738
+
739
+ qq.attach(self._element, 'drop', function(e){
740
+ if (!self._isValidFileDrag(e)) return;
741
+
742
+ e.preventDefault();
743
+ self._options.onDrop(e);
744
+ });
745
+ },
746
+ _isValidFileDrag: function(e){
747
+ var dt = e.dataTransfer,
748
+ // do not check dt.types.contains in webkit, because it crashes safari 4
749
+ isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1;
750
+
751
+ // dt.effectAllowed is none in Safari 5
752
+ // dt.types.contains check is for firefox
753
+ return dt && dt.effectAllowed != 'none' &&
754
+ (dt.files || (!isWebkit && dt.types.contains && dt.types.contains('Files')));
755
+
756
+ }
757
+ };
758
+
759
+ qq.UploadButton = function(o){
760
+ this._options = {
761
+ element: null,
762
+ // if set to true adds multiple attribute to file input
763
+ multiple: false,
764
+ // name attribute of file input
765
+ name: 'file',
766
+ onChange: function(input){},
767
+ hoverClass: 'qq-upload-button-hover',
768
+ focusClass: 'qq-upload-button-focus'
769
+ };
770
+
771
+ qq.extend(this._options, o);
772
+
773
+ this._element = this._options.element;
774
+
775
+ // make button suitable container for input
776
+ qq.css(this._element, {
777
+ position: 'relative',
778
+ overflow: 'hidden',
779
+ // Make sure browse button is in the right side
780
+ // in Internet Explorer
781
+ direction: 'ltr'
782
+ });
783
+
784
+ this._input = this._createInput();
785
+ };
786
+
787
+ qq.UploadButton.prototype = {
788
+ /* returns file input element */
789
+ getInput: function(){
790
+ return this._input;
791
+ },
792
+ /* cleans/recreates the file input */
793
+ reset: function(){
794
+ if (this._input.parentNode){
795
+ qq.remove(this._input);
796
+ }
797
+
798
+ qq.removeClass(this._element, this._options.focusClass);
799
+ this._input = this._createInput();
800
+ },
801
+ _createInput: function(){
802
+ var input = document.createElement("input");
803
+
804
+ if (this._options.multiple){
805
+ input.setAttribute("multiple", "multiple");
806
+ }
807
+
808
+ input.setAttribute("type", "file");
809
+ input.setAttribute("name", this._options.name);
810
+
811
+ qq.css(input, {
812
+ position: 'absolute',
813
+ // in Opera only 'browse' button
814
+ // is clickable and it is located at
815
+ // the right side of the input
816
+ right: 0,
817
+ top: 0,
818
+ /*width: '1000px',
819
+ height: '1000px',*/
820
+ fontFamily: 'Arial',
821
+ // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118
822
+ fontSize: '118px',
823
+ margin: 0,
824
+ padding: 0,
825
+ cursor: 'pointer',
826
+ opacity: 0/*,
827
+ width: $(this._element).outerWidth(),
828
+ height: $(this._element).outerHeight()*/
829
+ });
830
+
831
+ this._element.appendChild(input);
832
+
833
+ var self = this;
834
+ qq.attach(input, 'change', function(){
835
+ self._options.onChange(input);
836
+ });
837
+
838
+ qq.attach(input, 'mouseover', function(){
839
+ qq.addClass(self._element, self._options.hoverClass);
840
+ });
841
+ qq.attach(input, 'mouseout', function(){
842
+ qq.removeClass(self._element, self._options.hoverClass);
843
+ });
844
+ qq.attach(input, 'focus', function(){
845
+ qq.addClass(self._element, self._options.focusClass);
846
+ });
847
+ qq.attach(input, 'blur', function(){
848
+ qq.removeClass(self._element, self._options.focusClass);
849
+ });
850
+
851
+ // IE and Opera, unfortunately have 2 tab stops on file input
852
+ // which is unacceptable in our case, disable keyboard access
853
+ if (window.attachEvent){
854
+ // it is IE or Opera
855
+ input.setAttribute('tabIndex', "-1");
856
+ }
857
+
858
+ return input;
859
+ }
860
+ };
861
+
862
+ /**
863
+ * Class for uploading files, uploading itself is handled by child classes
864
+ */
865
+ qq.UploadHandlerAbstract = function(o){
866
+ this._options = {
867
+ debug: false,
868
+ action: '/upload.php',
869
+ // maximum number of concurrent uploads
870
+ maxConnections: 999,
871
+ onProgress: function(id, fileName, loaded, total){},
872
+ onComplete: function(id, fileName, response){},
873
+ onCancel: function(id, fileName){}
874
+ };
875
+ qq.extend(this._options, o);
876
+
877
+ this._queue = [];
878
+ // params for files in queue
879
+ this._params = [];
880
+ };
881
+ qq.UploadHandlerAbstract.prototype = {
882
+ log: function(str){
883
+ if (this._options.debug && window.console) console.log('[uploader] ' + str);
884
+ },
885
+ /**
886
+ * Adds file or file input to the queue
887
+ * @returns id
888
+ **/
889
+ add: function(file){},
890
+ /**
891
+ * Sends the file identified by id and additional query params to the server
892
+ */
893
+ upload: function(id, params){
894
+ var len = this._queue.push(id);
895
+
896
+ var copy = {};
897
+ qq.extend(copy, params);
898
+ this._params[id] = copy;
899
+
900
+ // if too many active uploads, wait...
901
+ if (len <= this._options.maxConnections){
902
+ this._upload(id, this._params[id]);
903
+ }
904
+ },
905
+ /**
906
+ * Cancels file upload by id
907
+ */
908
+ cancel: function(id){
909
+ this._cancel(id);
910
+ this._dequeue(id);
911
+ },
912
+ /**
913
+ * Cancells all uploads
914
+ */
915
+ cancelAll: function(){
916
+ for (var i=0; i<this._queue.length; i++){
917
+ this._cancel(this._queue[i]);
918
+ }
919
+ this._queue = [];
920
+ },
921
+ /**
922
+ * Returns name of the file identified by id
923
+ */
924
+ getName: function(id){},
925
+ /**
926
+ * Returns size of the file identified by id
927
+ */
928
+ getSize: function(id){},
929
+ /**
930
+ * Returns id of files being uploaded or
931
+ * waiting for their turn
932
+ */
933
+ getQueue: function(){
934
+ return this._queue;
935
+ },
936
+ /**
937
+ * Actual upload method
938
+ */
939
+ _upload: function(id){},
940
+ /**
941
+ * Actual cancel method
942
+ */
943
+ _cancel: function(id){},
944
+ /**
945
+ * Removes element from queue, starts upload of next
946
+ */
947
+ _dequeue: function(id){
948
+ var i = qq.indexOf(this._queue, id);
949
+ this._queue.splice(i, 1);
950
+
951
+ var max = this._options.maxConnections;
952
+
953
+ if (this._queue.length >= max){
954
+ var nextId = this._queue[max-1];
955
+ this._upload(nextId, this._params[nextId]);
956
+ }
957
+ }
958
+ };
959
+
960
+ /**
961
+ * Class for uploading files using form and iframe
962
+ * @inherits qq.UploadHandlerAbstract
963
+ */
964
+ qq.UploadHandlerForm = function(o){
965
+ qq.UploadHandlerAbstract.apply(this, arguments);
966
+
967
+ this._inputs = {};
968
+ };
969
+ // @inherits qq.UploadHandlerAbstract
970
+ qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype);
971
+
972
+ qq.extend(qq.UploadHandlerForm.prototype, {
973
+ add: function(fileInput){
974
+ fileInput.setAttribute('name', 'qqfile');
975
+ var id = 'qq-upload-handler-iframe' + qq.getUniqueId();
976
+
977
+ this._inputs[id] = fileInput;
978
+
979
+ // remove file input from DOM
980
+ if (fileInput.parentNode){
981
+ qq.remove(fileInput);
982
+ }
983
+
984
+ return id;
985
+ },
986
+ getName: function(id){
987
+ // get input value and remove path to normalize
988
+ return this._inputs[id].value.replace(/.*(\/|\\)/, "");
989
+ },
990
+ _cancel: function(id){
991
+ this._options.onCancel(id, this.getName(id));
992
+
993
+ delete this._inputs[id];
994
+
995
+ var iframe = document.getElementById(id);
996
+ if (iframe){
997
+ // to cancel request set src to something else
998
+ // we use src="javascript:false;" because it doesn't
999
+ // trigger ie6 prompt on https
1000
+ iframe.setAttribute('src', 'javascript:false;');
1001
+
1002
+ qq.remove(iframe);
1003
+ }
1004
+ },
1005
+ _upload: function(id, params){
1006
+ var input = this._inputs[id];
1007
+
1008
+ if (!input){
1009
+ throw new Error('file with passed id was not added, or already uploaded or cancelled');
1010
+ }
1011
+
1012
+ var fileName = this.getName(id);
1013
+
1014
+ var iframe = this._createIframe(id);
1015
+ var form = this._createForm(iframe, params);
1016
+ form.appendChild(input);
1017
+
1018
+ var self = this;
1019
+ this._attachLoadEvent(iframe, function(){
1020
+ self.log('iframe loaded');
1021
+
1022
+ var response = self._getIframeContentJSON(iframe);
1023
+
1024
+ self._options.onComplete(id, fileName, response);
1025
+ self._dequeue(id);
1026
+
1027
+ delete self._inputs[id];
1028
+ // timeout added to fix busy state in FF3.6
1029
+ setTimeout(function(){
1030
+ qq.remove(iframe);
1031
+ }, 1);
1032
+ });
1033
+
1034
+ form.submit();
1035
+ qq.remove(form);
1036
+
1037
+ return id;
1038
+ },
1039
+ _attachLoadEvent: function(iframe, callback){
1040
+ qq.attach(iframe, 'load', function(){
1041
+ // when we remove iframe from dom
1042
+ // the request stops, but in IE load
1043
+ // event fires
1044
+ if (!iframe.parentNode){
1045
+ return;
1046
+ }
1047
+
1048
+ // fixing Opera 10.53
1049
+ if (iframe.contentDocument &&
1050
+ iframe.contentDocument.body &&
1051
+ iframe.contentDocument.body.innerHTML == "false"){
1052
+ // In Opera event is fired second time
1053
+ // when body.innerHTML changed from false
1054
+ // to server response approx. after 1 sec
1055
+ // when we upload file with iframe
1056
+ return;
1057
+ }
1058
+
1059
+ callback();
1060
+ });
1061
+ },
1062
+ /**
1063
+ * Returns json object received by iframe from server.
1064
+ */
1065
+ _getIframeContentJSON: function(iframe){
1066
+ // iframe.contentWindow.document - for IE<7
1067
+ var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document,
1068
+ response;
1069
+
1070
+ this.log("converting iframe's innerHTML to JSON");
1071
+ this.log("innerHTML = " + doc.body.innerHTML);
1072
+
1073
+ try {
1074
+ response = eval("(" + doc.body.innerHTML + ")");
1075
+ } catch(err){
1076
+ response = {};
1077
+ }
1078
+
1079
+ return response;
1080
+ },
1081
+ /**
1082
+ * Creates iframe with unique name
1083
+ */
1084
+ _createIframe: function(id){
1085
+ // We can't use following code as the name attribute
1086
+ // won't be properly registered in IE6, and new window
1087
+ // on form submit will open
1088
+ // var iframe = document.createElement('iframe');
1089
+ // iframe.setAttribute('name', id);
1090
+
1091
+ var iframe = qq.toElement('<iframe src="javascript:false;" name="' + id + '" />');
1092
+ // src="javascript:false;" removes ie6 prompt on https
1093
+
1094
+ iframe.setAttribute('id', id);
1095
+
1096
+ iframe.style.display = 'none';
1097
+ document.body.appendChild(iframe);
1098
+
1099
+ return iframe;
1100
+ },
1101
+ /**
1102
+ * Creates form, that will be submitted to iframe
1103
+ */
1104
+ _createForm: function(iframe, params){
1105
+ // We can't use the following code in IE6
1106
+ // var form = document.createElement('form');
1107
+ // form.setAttribute('method', 'post');
1108
+ // form.setAttribute('enctype', 'multipart/form-data');
1109
+ // Because in this case file won't be attached to request
1110
+ var form = qq.toElement('<form method="post" enctype="multipart/form-data"></form>');
1111
+
1112
+ var queryString = qq.obj2url(params, this._options.action);
1113
+
1114
+ form.setAttribute('action', queryString);
1115
+ form.setAttribute('target', iframe.name);
1116
+ form.style.display = 'none';
1117
+ document.body.appendChild(form);
1118
+
1119
+ return form;
1120
+ }
1121
+ });
1122
+
1123
+ /**
1124
+ * Class for uploading files using xhr
1125
+ * @inherits qq.UploadHandlerAbstract
1126
+ */
1127
+ qq.UploadHandlerXhr = function(o){
1128
+ qq.UploadHandlerAbstract.apply(this, arguments);
1129
+
1130
+ this._files = [];
1131
+ this._xhrs = [];
1132
+
1133
+ // current loaded size in bytes for each file
1134
+ this._loaded = [];
1135
+ };
1136
+
1137
+ // static method
1138
+ qq.UploadHandlerXhr.isSupported = function(){
1139
+ var input = document.createElement('input');
1140
+ input.type = 'file';
1141
+
1142
+ return (
1143
+ 'multiple' in input &&
1144
+ typeof File != "undefined" &&
1145
+ typeof (new XMLHttpRequest()).upload != "undefined" );
1146
+ };
1147
+
1148
+ // @inherits qq.UploadHandlerAbstract
1149
+ qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype)
1150
+
1151
+ qq.extend(qq.UploadHandlerXhr.prototype, {
1152
+ /**
1153
+ * Adds file to the queue
1154
+ * Returns id to use with upload, cancel
1155
+ **/
1156
+ add: function(file){
1157
+ if (!(file instanceof File)){
1158
+ throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)');
1159
+ }
1160
+
1161
+ return this._files.push(file) - 1;
1162
+ },
1163
+ getName: function(id){
1164
+ var file = this._files[id];
1165
+ // fix missing name in Safari 4
1166
+ return file.fileName != null ? file.fileName : file.name;
1167
+ },
1168
+ getSize: function(id){
1169
+ var file = this._files[id];
1170
+ return file.fileSize != null ? file.fileSize : file.size;
1171
+ },
1172
+ /**
1173
+ * Returns uploaded bytes for file identified by id
1174
+ */
1175
+ getLoaded: function(id){
1176
+ return this._loaded[id] || 0;
1177
+ },
1178
+ /**
1179
+ * Sends the file identified by id and additional query params to the server
1180
+ * @param {Object} params name-value string pairs
1181
+ */
1182
+ _upload: function(id, params){
1183
+ var file = this._files[id],
1184
+ name = this.getName(id),
1185
+ size = this.getSize(id);
1186
+
1187
+ this._loaded[id] = 0;
1188
+
1189
+ var xhr = this._xhrs[id] = new XMLHttpRequest();
1190
+ var self = this;
1191
+
1192
+ xhr.upload.onprogress = function(e){
1193
+ if (e.lengthComputable){
1194
+ self._loaded[id] = e.loaded;
1195
+ self._options.onProgress(id, name, e.loaded, e.total);
1196
+ }
1197
+ };
1198
+
1199
+ xhr.onreadystatechange = function(){
1200
+ if (xhr.readyState == 4){
1201
+ self._onComplete(id, xhr);
1202
+ }
1203
+ };
1204
+
1205
+ // build query string
1206
+ params = params || {};
1207
+ params['qqfile'] = name;
1208
+ var queryString = qq.obj2url(params, this._options.action);
1209
+
1210
+ xhr.open("POST", queryString, true);
1211
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
1212
+ xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
1213
+ xhr.setRequestHeader("Content-Type", "application/octet-stream");
1214
+ xhr.send(file);
1215
+ },
1216
+ _onComplete: function(id, xhr){
1217
+ // the request was aborted/cancelled
1218
+ if (!this._files[id]) return;
1219
+
1220
+ var name = this.getName(id);
1221
+ var size = this.getSize(id);
1222
+
1223
+ this._options.onProgress(id, name, size, size);
1224
+
1225
+ if (xhr.status == 200){
1226
+ this.log("xhr - server response received");
1227
+ this.log("responseText = " + xhr.responseText);
1228
+
1229
+ var response;
1230
+
1231
+ try {
1232
+ response = eval("(" + xhr.responseText + ")");
1233
+ } catch(err){
1234
+ response = {error: xhr.responseText};
1235
+ }
1236
+
1237
+ this._options.onComplete(id, name, response);
1238
+
1239
+ } else {
1240
+ this._options.onComplete(id, name, {});
1241
+ }
1242
+
1243
+ this._files[id] = null;
1244
+ this._xhrs[id] = null;
1245
+ this._dequeue(id);
1246
+ },
1247
+ _cancel: function(id){
1248
+ this._options.onCancel(id, this.getName(id));
1249
+
1250
+ this._files[id] = null;
1251
+
1252
+ if (this._xhrs[id]){
1253
+ this._xhrs[id].abort();
1254
+ this._xhrs[id] = null;
1255
+ }
1256
+ }
1257
+ });
js/jquery/history.adapter.jquery.js ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * History.js jQuery Adapter
3
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
4
+ * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
5
+ * @license New BSD License <http://creativecommons.org/licenses/BSD/>
6
+ */
7
+
8
+ // Closure
9
+ (function(window,undefined){
10
+ // Localise Globals
11
+ var
12
+ History = window.History = window.History||{},
13
+ jQuery = window.jQuery;
14
+
15
+ // Check Existence
16
+ if ( typeof History.Adapter !== 'undefined' ) {
17
+ throw new Error('History.js Adapter has already been loaded...');
18
+ }
19
+
20
+ // Add the Adapter
21
+ History.Adapter = {
22
+ /**
23
+ * History.Adapter.bind(el,event,callback)
24
+ * @param {Element|Selector} el
25
+ * @param {String} event - custom and standard events
26
+ * @param {Function} callback
27
+ * @return
28
+ */
29
+ bind: function(el,event,callback){
30
+ jQuery(el).bind(event,callback);
31
+ },
32
+
33
+ /**
34
+ * History.Adapter.trigger(el,event)
35
+ * @param {Element|Selector} el
36
+ * @param {String} event - custom and standard events
37
+ * @return
38
+ */
39
+ trigger: function(el,event){
40
+ jQuery(el).trigger(event);
41
+ },
42
+
43
+ /**
44
+ * History.Adapter.trigger(el,event,data)
45
+ * @param {Function} callback
46
+ * @return
47
+ */
48
+ onDomLoad: function(callback) {
49
+ jQuery(callback);
50
+ }
51
+ };
52
+
53
+ // Try and Initialise History
54
+ if ( typeof History.init !== 'undefined' ) {
55
+ History.init();
56
+ }
57
+
58
+ })(window);
js/jquery/history.js ADDED
@@ -0,0 +1,1866 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * History.js Core
3
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
4
+ * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
5
+ * @license New BSD License <http://creativecommons.org/licenses/BSD/>
6
+ */
7
+
8
+ (function(window, jQuery, Object, undefined){
9
+ "use strict";
10
+
11
+ // --------------------------------------------------------------------------
12
+ // Initialise
13
+
14
+ // Localise Globals
15
+ var
16
+ console = window.console||undefined, // Prevent a JSLint complain
17
+ document = window.document, // Make sure we are using the correct document
18
+ navigator = window.navigator, // Make sure we are using the correct navigator
19
+ amplify = window.amplify||false, // Amplify.js
20
+ setTimeout = window.setTimeout,
21
+ clearTimeout = window.clearTimeout,
22
+ setInterval = window.setInterval,
23
+ clearInterval = window.clearInterval,
24
+ JSON = window.JSON || {},
25
+ History = window.History = window.History||{}, // Public History Object
26
+ history = window.history; // Old History Object
27
+
28
+ // MooTools Compatibility
29
+ JSON.stringify = JSON.stringify||JSON.encode || Object.toJSON;
30
+ JSON.parse = JSON.parse||JSON.decode || jQuery.parseJSON;
31
+
32
+ // Check Existence
33
+ if ( typeof History.init !== 'undefined' ) {
34
+ throw new Error('History.js Core has already been loaded...');
35
+ }
36
+
37
+ // Initialise History
38
+ History.init = function(){
39
+ // Check Load Status of Adapter
40
+ if ( typeof History.Adapter === 'undefined' ) {
41
+ return false;
42
+ }
43
+
44
+ // Check Load Status of Core
45
+ if ( typeof History.initCore !== 'undefined' ) {
46
+ History.initCore();
47
+ }
48
+
49
+ // Check Load Status of HTML4 Support
50
+ if ( typeof History.initHtml4 !== 'undefined' ) {
51
+ History.initHtml4();
52
+ }
53
+
54
+ // Return true
55
+ return true;
56
+ };
57
+
58
+ // --------------------------------------------------------------------------
59
+ // Initialise Core
60
+
61
+ // Initialise Core
62
+ History.initCore = function(){
63
+ // Initialise
64
+ if ( typeof History.initCore.initialized !== 'undefined' ) {
65
+ // Already Loaded
66
+ return false;
67
+ }
68
+ else {
69
+ History.initCore.initialized = true;
70
+ }
71
+
72
+ // ----------------------------------------------------------------------
73
+ // Options
74
+
75
+ /**
76
+ * History.options
77
+ * Configurable options
78
+ */
79
+ History.options = History.options||{};
80
+
81
+ /**
82
+ * History.options.hashChangeInterval
83
+ * How long should the interval be before hashchange checks
84
+ */
85
+ History.options.hashChangeInterval = History.options.hashChangeInterval || 100;
86
+
87
+ /**
88
+ * History.options.safariPollInterval
89
+ * How long should the interval be before safari poll checks
90
+ */
91
+ History.options.safariPollInterval = History.options.safariPollInterval || 500;
92
+
93
+ /**
94
+ * History.options.doubleCheckInterval
95
+ * How long should the interval be before we perform a double check
96
+ */
97
+ History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500;
98
+
99
+ /**
100
+ * History.options.storeInterval
101
+ * How long should we wait between store calls
102
+ */
103
+ History.options.storeInterval = History.options.storeInterval || 1000;
104
+
105
+ /**
106
+ * History.options.busyDelay
107
+ * How long should we wait between busy events
108
+ */
109
+ History.options.busyDelay = History.options.busyDelay || 250;
110
+
111
+ /**
112
+ * History.options.debug
113
+ * If true will enable debug messages to be logged
114
+ */
115
+ History.options.debug = History.options.debug || false;
116
+
117
+ /**
118
+ * History.options.initialTitle
119
+ * What is the title of the initial state
120
+ */
121
+ History.options.initialTitle = History.options.initialTitle || document.title;
122
+
123
+
124
+ // ----------------------------------------------------------------------
125
+ // Interval record
126
+
127
+ /**
128
+ * History.intervalList
129
+ * List of intervals set, to be cleared when document is unloaded.
130
+ */
131
+ History.intervalList = [];
132
+
133
+ /**
134
+ * History.clearAllIntervals
135
+ * Clears all setInterval instances.
136
+ */
137
+ History.clearAllIntervals = function(){
138
+ var i, il = History.intervalList;
139
+ if (typeof il !== "undefined" && il !== null) {
140
+ for (i = 0; i < il.length; i++) {
141
+ clearInterval(il[i]);
142
+ }
143
+ History.intervalList = null;
144
+ }
145
+ };
146
+ History.Adapter.bind(window,"beforeunload",History.clearAllIntervals);
147
+ History.Adapter.bind(window,"unload",History.clearAllIntervals);
148
+
149
+
150
+ // ----------------------------------------------------------------------
151
+ // Debug
152
+
153
+ /**
154
+ * History.debug(message,...)
155
+ * Logs the passed arguments if debug enabled
156
+ */
157
+ History.debug = function(){
158
+ if ( (History.options.debug||false) ) {
159
+ History.log.apply(History,arguments);
160
+ }
161
+ };
162
+
163
+ /**
164
+ * History.log(message,...)
165
+ * Logs the passed arguments
166
+ */
167
+ History.log = function(){
168
+ // Prepare
169
+ var
170
+ consoleExists = !(typeof console === 'undefined' || typeof console.log === 'undefined' || typeof console.log.apply === 'undefined'),
171
+ textarea = document.getElementById('log'),
172
+ message,
173
+ i,n
174
+ ;
175
+
176
+ // Write to Console
177
+ if ( consoleExists ) {
178
+ var args = Array.prototype.slice.call(arguments);
179
+ message = args.shift();
180
+ if ( typeof console.debug !== 'undefined' ) {
181
+ console.debug.apply(console,[message,args]);
182
+ }
183
+ else {
184
+ console.log.apply(console,[message,args]);
185
+ }
186
+ }
187
+ else {
188
+ message = ("\n"+arguments[0]+"\n");
189
+ }
190
+
191
+ // Write to log
192
+ for ( i=1,n=arguments.length; i<n; ++i ) {
193
+ var arg = arguments[i];
194
+ if ( typeof arg === 'object' && typeof JSON !== 'undefined' ) {
195
+ try {
196
+ arg = JSON.stringify(arg);
197
+ }
198
+ catch ( Exception ) {
199
+ // Recursive Object
200
+ }
201
+ }
202
+ message += "\n"+arg+"\n";
203
+ }
204
+
205
+ // Textarea
206
+ if ( textarea ) {
207
+ textarea.value += message+"\n-----\n";
208
+ textarea.scrollTop = textarea.scrollHeight - textarea.clientHeight;
209
+ }
210
+ // No Textarea, No Console
211
+ else if ( !consoleExists ) {
212
+ alert(message);
213
+ }
214
+
215
+ // Return true
216
+ return true;
217
+ };
218
+
219
+ // ----------------------------------------------------------------------
220
+ // Emulated Status
221
+
222
+ /**
223
+ * History.getInternetExplorerMajorVersion()
224
+ * Get's the major version of Internet Explorer
225
+ * @return {integer}
226
+ * @license Public Domain
227
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
228
+ * @author James Padolsey <https://gist.github.com/527683>
229
+ */
230
+ History.getInternetExplorerMajorVersion = function(){
231
+ var result = History.getInternetExplorerMajorVersion.cached =
232
+ (typeof History.getInternetExplorerMajorVersion.cached !== 'undefined')
233
+ ? History.getInternetExplorerMajorVersion.cached
234
+ : (function(){
235
+ var v = 3,
236
+ div = document.createElement('div'),
237
+ all = div.getElementsByTagName('i');
238
+ while ( (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->') && all[0] ) {}
239
+ return (v > 4) ? v : false;
240
+ })()
241
+ ;
242
+ return result;
243
+ };
244
+
245
+ /**
246
+ * History.isInternetExplorer()
247
+ * Are we using Internet Explorer?
248
+ * @return {boolean}
249
+ * @license Public Domain
250
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
251
+ */
252
+ History.isInternetExplorer = function(){
253
+ var result =
254
+ History.isInternetExplorer.cached =
255
+ (typeof History.isInternetExplorer.cached !== 'undefined')
256
+ ? History.isInternetExplorer.cached
257
+ : Boolean(History.getInternetExplorerMajorVersion())
258
+ ;
259
+ return result;
260
+ };
261
+
262
+ /**
263
+ * History.emulated
264
+ * Which features require emulating?
265
+ */
266
+ History.emulated = {
267
+ pushState: !Boolean(
268
+ window.history && window.history.pushState && window.history.replaceState
269
+ && !(
270
+ (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent) /* disable for versions of iOS before version 4.3 (8F190) */
271
+ || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent) /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
272
+ )
273
+ ),
274
+ hashChange: Boolean(
275
+ !(('onhashchange' in window) || ('onhashchange' in document))
276
+ ||
277
+ (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8)
278
+ )
279
+ };
280
+
281
+ /**
282
+ * History.enabled
283
+ * Is History enabled?
284
+ */
285
+ History.enabled = !History.emulated.pushState;
286
+
287
+ /**
288
+ * History.bugs
289
+ * Which bugs are present
290
+ */
291
+ History.bugs = {
292
+ /**
293
+ * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call
294
+ * https://bugs.webkit.org/show_bug.cgi?id=56249
295
+ */
296
+ setHash: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
297
+
298
+ /**
299
+ * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions
300
+ * https://bugs.webkit.org/show_bug.cgi?id=42940
301
+ */
302
+ safariPoll: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
303
+
304
+ /**
305
+ * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)
306
+ */
307
+ ieDoubleCheck: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8),
308
+
309
+ /**
310
+ * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event
311
+ */
312
+ hashEscape: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 7)
313
+ };
314
+
315
+ /**
316
+ * History.isEmptyObject(obj)
317
+ * Checks to see if the Object is Empty
318
+ * @param {Object} obj
319
+ * @return {boolean}
320
+ */
321
+ History.isEmptyObject = function(obj) {
322
+ for ( var name in obj ) {
323
+ return false;
324
+ }
325
+ return true;
326
+ };
327
+
328
+ /**
329
+ * History.cloneObject(obj)
330
+ * Clones a object
331
+ * @param {Object} obj
332
+ * @return {Object}
333
+ */
334
+ History.cloneObject = function(obj) {
335
+ var hash,newObj;
336
+ if ( obj ) {
337
+ hash = JSON.stringify(obj);
338
+ newObj = JSON.parse(hash);
339
+ }
340
+ else {
341
+ newObj = {};
342
+ }
343
+ return newObj;
344
+ };
345
+
346
+ // ----------------------------------------------------------------------
347
+ // URL Helpers
348
+
349
+ /**
350
+ * History.getRootUrl()
351
+ * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com"
352
+ * @return {String} rootUrl
353
+ */
354
+ History.getRootUrl = function(){
355
+ // Create
356
+ var rootUrl = document.location.protocol+'//'+(document.location.hostname||document.location.host);
357
+ if ( document.location.port||false ) {
358
+ rootUrl += ':'+document.location.port;
359
+ }
360
+ rootUrl += '/';
361
+
362
+ // Return
363
+ return rootUrl;
364
+ };
365
+
366
+ /**
367
+ * History.getBaseHref()
368
+ * Fetches the `href` attribute of the `<base href="...">` element if it exists
369
+ * @return {String} baseHref
370
+ */
371
+ History.getBaseHref = function(){
372
+ // Create
373
+ var
374
+ baseElements = document.getElementsByTagName('base'),
375
+ baseElement = null,
376
+ baseHref = '';
377
+
378
+ // Test for Base Element
379
+ if ( baseElements.length === 1 ) {
380
+ // Prepare for Base Element
381
+ baseElement = baseElements[0];
382
+ baseHref = baseElement.href.replace(/[^\/]+$/,'');
383
+ }
384
+
385
+ // Adjust trailing slash
386
+ baseHref = baseHref.replace(/\/+$/,'');
387
+ if ( baseHref ) baseHref += '/';
388
+
389
+ // Return
390
+ return baseHref;
391
+ };
392
+
393
+ /**
394
+ * History.getBaseUrl()
395
+ * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first)
396
+ * @return {String} baseUrl
397
+ */
398
+ History.getBaseUrl = function(){
399
+ // Create
400
+ var baseUrl = History.getBaseHref()||History.getBasePageUrl()||History.getRootUrl();
401
+
402
+ // Return
403
+ return baseUrl;
404
+ };
405
+
406
+ /**
407
+ * History.getPageUrl()
408
+ * Fetches the URL of the current page
409
+ * @return {String} pageUrl
410
+ */
411
+ History.getPageUrl = function(){
412
+ // Fetch
413
+ var
414
+ State = History.getState(false,false),
415
+ stateUrl = (State||{}).url||document.location.href;
416
+
417
+ // Create
418
+ var pageUrl = stateUrl.replace(/\/+$/,'').replace(/[^\/]+$/,function(part,index,string){
419
+ return (/\./).test(part) ? part : part+'/';
420
+ });
421
+
422
+ // Return
423
+ return pageUrl;
424
+ };
425
+
426
+ /**
427
+ * History.getBasePageUrl()
428
+ * Fetches the Url of the directory of the current page
429
+ * @return {String} basePageUrl
430
+ */
431
+ History.getBasePageUrl = function(){
432
+ // Create
433
+ var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
434
+ return (/[^\/]$/).test(part) ? '' : part;
435
+ }).replace(/\/+$/,'')+'/';
436
+
437
+ // Return
438
+ return basePageUrl;
439
+ };
440
+
441
+ /**
442
+ * History.getFullUrl(url)
443
+ * Ensures that we have an absolute URL and not a relative URL
444
+ * @param {string} url
445
+ * @param {Boolean} allowBaseHref
446
+ * @return {string} fullUrl
447
+ */
448
+ History.getFullUrl = function(url,allowBaseHref){
449
+ // Prepare
450
+ var fullUrl = url, firstChar = url.substring(0,1);
451
+ allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref;
452
+
453
+ // Check
454
+ if ( /[a-z]+\:\/\//.test(url) ) {
455
+ // Full URL
456
+ }
457
+ else if ( firstChar === '/' ) {
458
+ // Root URL
459
+ fullUrl = History.getRootUrl()+url.replace(/^\/+/,'');
460
+ }
461
+ else if ( firstChar === '#' ) {
462
+ // Anchor URL
463
+ fullUrl = History.getPageUrl().replace(/#.*/,'')+url;
464
+ }
465
+ else if ( firstChar === '?' ) {
466
+ // Query URL
467
+ fullUrl = History.getPageUrl().replace(/[\?#].*/,'')+url;
468
+ }
469
+ else {
470
+ // Relative URL
471
+ if ( allowBaseHref ) {
472
+ fullUrl = History.getBaseUrl()+url.replace(/^(\.\/)+/,'');
473
+ } else {
474
+ fullUrl = History.getBasePageUrl()+url.replace(/^(\.\/)+/,'');
475
+ }
476
+ // We have an if condition above as we do not want hashes
477
+ // which are relative to the baseHref in our URLs
478
+ // as if the baseHref changes, then all our bookmarks
479
+ // would now point to different locations
480
+ // whereas the basePageUrl will always stay the same
481
+ }
482
+
483
+ // Return
484
+ return fullUrl.replace(/\#$/,'');
485
+ };
486
+
487
+ /**
488
+ * History.getShortUrl(url)
489
+ * Ensures that we have a relative URL and not a absolute URL
490
+ * @param {string} url
491
+ * @return {string} url
492
+ */
493
+ History.getShortUrl = function(url){
494
+ // Prepare
495
+ var shortUrl = url, baseUrl = History.getBaseUrl(), rootUrl = History.getRootUrl();
496
+
497
+ // Trim baseUrl
498
+ if ( History.emulated.pushState ) {
499
+ // We are in a if statement as when pushState is not emulated
500
+ // The actual url these short urls are relative to can change
501
+ // So within the same session, we the url may end up somewhere different
502
+ shortUrl = shortUrl.replace(baseUrl,'');
503
+ }
504
+
505
+ // Trim rootUrl
506
+ shortUrl = shortUrl.replace(rootUrl,'/');
507
+
508
+ // Ensure we can still detect it as a state
509
+ if ( History.isTraditionalAnchor(shortUrl) ) {
510
+ shortUrl = './'+shortUrl;
511
+ }
512
+
513
+ // Clean It
514
+ shortUrl = shortUrl.replace(/^(\.\/)+/g,'./').replace(/\#$/,'');
515
+
516
+ // Return
517
+ return shortUrl;
518
+ };
519
+
520
+ // ----------------------------------------------------------------------
521
+ // State Storage
522
+
523
+ /**
524
+ * History.store
525
+ * The store for all session specific data
526
+ */
527
+ History.store = amplify ? (amplify.store('History.store')||{}) : {};
528
+ History.store.idToState = History.store.idToState||{};
529
+ History.store.urlToId = History.store.urlToId||{};
530
+ History.store.stateToId = History.store.stateToId||{};
531
+
532
+ /**
533
+ * History.idToState
534
+ * 1-1: State ID to State Object
535
+ */
536
+ History.idToState = History.idToState||{};
537
+
538
+ /**
539
+ * History.stateToId
540
+ * 1-1: State String to State ID
541
+ */
542
+ History.stateToId = History.stateToId||{};
543
+
544
+ /**
545
+ * History.urlToId
546
+ * 1-1: State URL to State ID
547
+ */
548
+ History.urlToId = History.urlToId||{};
549
+
550
+ /**
551
+ * History.storedStates
552
+ * Store the states in an array
553
+ */
554
+ History.storedStates = History.storedStates||[];
555
+
556
+ /**
557
+ * History.savedStates
558
+ * Saved the states in an array
559
+ */
560
+ History.savedStates = History.savedStates||[];
561
+
562
+ /**
563
+ * History.getState()
564
+ * Get an object containing the data, title and url of the current state
565
+ * @param {Boolean} friendly
566
+ * @param {Boolean} create
567
+ * @return {Object} State
568
+ */
569
+ History.getState = function(friendly,create){
570
+ // Prepare
571
+ if ( typeof friendly === 'undefined' ) { friendly = true; }
572
+ if ( typeof create === 'undefined' ) { create = true; }
573
+
574
+ // Fetch
575
+ var State = History.getLastSavedState();
576
+
577
+ // Create
578
+ if ( !State && create ) {
579
+ State = History.createStateObject();
580
+ }
581
+
582
+ // Adjust
583
+ if ( friendly ) {
584
+ State = History.cloneObject(State);
585
+ State.url = State.cleanUrl||State.url;
586
+ }
587
+
588
+ // Return
589
+ return State;
590
+ };
591
+
592
+ /**
593
+ * History.getIdByState(State)
594
+ * Gets a ID for a State
595
+ * @param {State} newState
596
+ * @return {String} id
597
+ */
598
+ History.getIdByState = function(newState){
599
+
600
+ // Fetch ID
601
+ var id = History.extractId(newState.url);
602
+ if ( !id ) {
603
+ // Find ID via State String
604
+ var str = History.getStateString(newState);
605
+ if ( typeof History.stateToId[str] !== 'undefined' ) {
606
+ id = History.stateToId[str];
607
+ }
608
+ else if ( typeof History.store.stateToId[str] !== 'undefined' ) {
609
+ id = History.store.stateToId[str];
610
+ }
611
+ else {
612
+ // Generate a new ID
613
+ while ( true ) {
614
+ id = String(Math.floor(Math.random()*1000));
615
+ if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) {
616
+ break;
617
+ }
618
+ }
619
+
620
+ // Apply the new State to the ID
621
+ History.stateToId[str] = id;
622
+ History.idToState[id] = newState;
623
+ }
624
+ }
625
+
626
+ // Return ID
627
+ return id;
628
+ };
629
+
630
+ /**
631
+ * History.normalizeState(State)
632
+ * Expands a State Object
633
+ * @param {object} State
634
+ * @return {object}
635
+ */
636
+ History.normalizeState = function(oldState){
637
+ // Prepare
638
+ if ( !oldState || (typeof oldState !== 'object') ) {
639
+ oldState = {};
640
+ }
641
+
642
+ // Check
643
+ if ( typeof oldState.normalized !== 'undefined' ) {
644
+ return oldState;
645
+ }
646
+
647
+ // Adjust
648
+ if ( !oldState.data || (typeof oldState.data !== 'object') ) {
649
+ oldState.data = {};
650
+ }
651
+
652
+ // ----------------------------------------------------------------------
653
+
654
+ // Create
655
+ var newState = {};
656
+ newState.normalized = true;
657
+ newState.title = oldState.title||'';
658
+ newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));
659
+ newState.hash = History.getShortUrl(newState.url);
660
+ newState.data = History.cloneObject(oldState.data);
661
+
662
+ // Fetch ID
663
+ newState.id = History.getIdByState(newState);
664
+
665
+ // ----------------------------------------------------------------------
666
+
667
+ // Clean the URL
668
+ newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');
669
+ newState.url = newState.cleanUrl;
670
+
671
+ // Check to see if we have more than just a url
672
+ var dataNotEmpty = !History.isEmptyObject(newState.data);
673
+
674
+ // Apply
675
+ if ( newState.title || dataNotEmpty ) {
676
+ // Add ID to Hash
677
+ newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
678
+ if ( !/\?/.test(newState.hash) ) {
679
+ newState.hash += '?';
680
+ }
681
+ newState.hash += '&_suid='+newState.id;
682
+ }
683
+
684
+ // Create the Hashed URL
685
+ newState.hashedUrl = History.getFullUrl(newState.hash);
686
+
687
+ // ----------------------------------------------------------------------
688
+
689
+ // Update the URL if we have a duplicate
690
+ if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) {
691
+ newState.url = newState.hashedUrl;
692
+ }
693
+
694
+ // ----------------------------------------------------------------------
695
+
696
+ // Return
697
+ return newState;
698
+ };
699
+
700
+ /**
701
+ * History.createStateObject(data,title,url)
702
+ * Creates a object based on the data, title and url state params
703
+ * @param {object} data
704
+ * @param {string} title
705
+ * @param {string} url
706
+ * @return {object}
707
+ */
708
+ History.createStateObject = function(data,title,url){
709
+ // Hashify
710
+ var State = {
711
+ 'data': data,
712
+ 'title': title,
713
+ 'url': url
714
+ };
715
+
716
+ // Expand the State
717
+ State = History.normalizeState(State);
718
+
719
+ // Return object
720
+ return State;
721
+ };
722
+
723
+ /**
724
+ * History.getStateById(id)
725
+ * Get a state by it's UID
726
+ * @param {String} id
727
+ */
728
+ History.getStateById = function(id){
729
+ // Prepare
730
+ id = String(id);
731
+
732
+ // Retrieve
733
+ var State = History.idToState[id] || History.store.idToState[id] || undefined;
734
+
735
+ // Return State
736
+ return State;
737
+ };
738
+
739
+ /**
740
+ * Get a State's String
741
+ * @param {State} passedState
742
+ */
743
+ History.getStateString = function(passedState){
744
+ // Prepare
745
+ var State = History.normalizeState(passedState);
746
+
747
+ // Clean
748
+ var cleanedState = {
749
+ data: State.data,
750
+ title: passedState.title,
751
+ url: passedState.url
752
+ };
753
+
754
+ // Fetch
755
+ var str = JSON.stringify(cleanedState);
756
+
757
+ // Return
758
+ return str;
759
+ };
760
+
761
+ /**
762
+ * Get a State's ID
763
+ * @param {State} passedState
764
+ * @return {String} id
765
+ */
766
+ History.getStateId = function(passedState){
767
+ // Prepare
768
+ var State = History.normalizeState(passedState);
769
+
770
+ // Fetch
771
+ var id = State.id;
772
+
773
+ // Return
774
+ return id;
775
+ };
776
+
777
+ /**
778
+ * History.getHashByState(State)
779
+ * Creates a Hash for the State Object
780
+ * @param {State} passedState
781
+ * @return {String} hash
782
+ */
783
+ History.getHashByState = function(passedState){
784
+ // Prepare
785
+ var hash, State = History.normalizeState(passedState);
786
+
787
+ // Fetch
788
+ hash = State.hash;
789
+
790
+ // Return
791
+ return hash;
792
+ };
793
+
794
+ /**
795
+ * History.extractId(url_or_hash)
796
+ * Get a State ID by it's URL or Hash
797
+ * @param {string} url_or_hash
798
+ * @return {string} id
799
+ */
800
+ History.extractId = function ( url_or_hash ) {
801
+ // Prepare
802
+ var id;
803
+
804
+ // Extract
805
+ var parts,url;
806
+ parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash);
807
+ url = parts ? (parts[1]||url_or_hash) : url_or_hash;
808
+ id = parts ? String(parts[2]||'') : '';
809
+
810
+ // Return
811
+ return id||false;
812
+ };
813
+
814
+ /**
815
+ * History.isTraditionalAnchor
816
+ * Checks to see if the url is a traditional anchor or not
817
+ * @param {String} url_or_hash
818
+ * @return {Boolean}
819
+ */
820
+ History.isTraditionalAnchor = function(url_or_hash){
821
+ // Check
822
+ var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
823
+
824
+ // Return
825
+ return isTraditional;
826
+ };
827
+
828
+ /**
829
+ * History.extractState
830
+ * Get a State by it's URL or Hash
831
+ * @param {String} url_or_hash
832
+ * @return {State|null}
833
+ */
834
+ History.extractState = function(url_or_hash,create){
835
+ // Prepare
836
+ var State = null;
837
+ create = create||false;
838
+
839
+ // Fetch SUID
840
+ var id = History.extractId(url_or_hash);
841
+ if ( id ) {
842
+ State = History.getStateById(id);
843
+ }
844
+
845
+ // Fetch SUID returned no State
846
+ if ( !State ) {
847
+ // Fetch URL
848
+ var url = History.getFullUrl(url_or_hash);
849
+
850
+ // Check URL
851
+ id = History.getIdByUrl(url)||false;
852
+ if ( id ) {
853
+ State = History.getStateById(id);
854
+ }
855
+
856
+ // Create State
857
+ if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) {
858
+ State = History.createStateObject(null,null,url);
859
+ }
860
+ }
861
+
862
+ // Return
863
+ return State;
864
+ };
865
+
866
+ /**
867
+ * History.getIdByUrl()
868
+ * Get a State ID by a State URL
869
+ */
870
+ History.getIdByUrl = function(url){
871
+ // Fetch
872
+ var id = History.urlToId[url] || History.store.urlToId[url] || undefined;
873
+
874
+ // Return
875
+ return id;
876
+ };
877
+
878
+ /**
879
+ * History.getLastSavedState()
880
+ * Get an object containing the data, title and url of the current state
881
+ * @return {Object} State
882
+ */
883
+ History.getLastSavedState = function(){
884
+ return History.savedStates[History.savedStates.length-1]||undefined;
885
+ };
886
+
887
+ /**
888
+ * History.getLastStoredState()
889
+ * Get an object containing the data, title and url of the current state
890
+ * @return {Object} State
891
+ */
892
+ History.getLastStoredState = function(){
893
+ return History.storedStates[History.storedStates.length-1]||undefined;
894
+ };
895
+
896
+ /**
897
+ * History.hasUrlDuplicate
898
+ * Checks if a Url will have a url conflict
899
+ * @param {Object} newState
900
+ * @return {Boolean} hasDuplicate
901
+ */
902
+ History.hasUrlDuplicate = function(newState) {
903
+ // Prepare
904
+ var hasDuplicate = false;
905
+
906
+ // Fetch
907
+ var oldState = History.extractState(newState.url);
908
+
909
+ // Check
910
+ hasDuplicate = oldState && oldState.id !== newState.id;
911
+
912
+ // Return
913
+ return hasDuplicate;
914
+ };
915
+
916
+ /**
917
+ * History.storeState
918
+ * Store a State
919
+ * @param {Object} newState
920
+ * @return {Object} newState
921
+ */
922
+ History.storeState = function(newState){
923
+ // Store the State
924
+ History.urlToId[newState.url] = newState.id;
925
+
926
+ // Push the State
927
+ History.storedStates.push(History.cloneObject(newState));
928
+
929
+ // Return newState
930
+ return newState;
931
+ };
932
+
933
+ /**
934
+ * History.isLastSavedState(newState)
935
+ * Tests to see if the state is the last state
936
+ * @param {Object} newState
937
+ * @return {boolean} isLast
938
+ */
939
+ History.isLastSavedState = function(newState){
940
+ // Prepare
941
+ var isLast = false;
942
+
943
+ // Check
944
+ if ( History.savedStates.length ) {
945
+ var
946
+ newId = newState.id,
947
+ oldState = History.getLastSavedState(),
948
+ oldId = oldState.id;
949
+
950
+ // Check
951
+ isLast = (newId === oldId);
952
+ }
953
+
954
+ // Return
955
+ return isLast;
956
+ };
957
+
958
+ /**
959
+ * History.saveState
960
+ * Push a State
961
+ * @param {Object} newState
962
+ * @return {boolean} changed
963
+ */
964
+ History.saveState = function(newState){
965
+ // Check Hash
966
+ if ( History.isLastSavedState(newState) ) {
967
+ return false;
968
+ }
969
+
970
+ // Push the State
971
+ History.savedStates.push(History.cloneObject(newState));
972
+
973
+ // Return true
974
+ return true;
975
+ };
976
+
977
+ /**
978
+ * History.getStateByIndex()
979
+ * Gets a state by the index
980
+ * @param {integer} index
981
+ * @return {Object}
982
+ */
983
+ History.getStateByIndex = function(index){
984
+ // Prepare
985
+ var State = null;
986
+
987
+ // Handle
988
+ if ( typeof index === 'undefined' ) {
989
+ // Get the last inserted
990
+ State = History.savedStates[History.savedStates.length-1];
991
+ }
992
+ else if ( index < 0 ) {
993
+ // Get from the end
994
+ State = History.savedStates[History.savedStates.length+index];
995
+ }
996
+ else {
997
+ // Get from the beginning
998
+ State = History.savedStates[index];
999
+ }
1000
+
1001
+ // Return State
1002
+ return State;
1003
+ };
1004
+
1005
+ // ----------------------------------------------------------------------
1006
+ // Hash Helpers
1007
+
1008
+ /**
1009
+ * History.getHash()
1010
+ * Gets the current document hash
1011
+ * @return {string}
1012
+ */
1013
+ History.getHash = function(){
1014
+ var hash = History.unescapeHash(document.location.hash);
1015
+ return hash;
1016
+ };
1017
+
1018
+ /**
1019
+ * History.unescapeString()
1020
+ * Unescape a string
1021
+ * @param {String} str
1022
+ * @return {string}
1023
+ */
1024
+ History.unescapeString = function(str){
1025
+ // Prepare
1026
+ var result = str;
1027
+
1028
+ // Unescape hash
1029
+ var tmp;
1030
+ while ( true ) {
1031
+ tmp = window.unescape(result);
1032
+ if ( tmp === result ) {
1033
+ break;
1034
+ }
1035
+ result = tmp;
1036
+ }
1037
+
1038
+ // Return result
1039
+ return result;
1040
+ };
1041
+
1042
+ /**
1043
+ * History.unescapeHash()
1044
+ * normalize and Unescape a Hash
1045
+ * @param {String} hash
1046
+ * @return {string}
1047
+ */
1048
+ History.unescapeHash = function(hash){
1049
+ // Prepare
1050
+ var result = History.normalizeHash(hash);
1051
+
1052
+ // Unescape hash
1053
+ result = History.unescapeString(result);
1054
+
1055
+ // Return result
1056
+ return result;
1057
+ };
1058
+
1059
+ /**
1060
+ * History.normalizeHash()
1061
+ * normalize a hash across browsers
1062
+ * @return {string}
1063
+ */
1064
+ History.normalizeHash = function(hash){
1065
+ var result = hash.replace(/[^#]*#/,'').replace(/#.*/, '');
1066
+
1067
+ // Return result
1068
+ return result;
1069
+ };
1070
+
1071
+ /**
1072
+ * History.setHash(hash)
1073
+ * Sets the document hash
1074
+ * @param {string} hash
1075
+ * @return {History}
1076
+ */
1077
+ History.setHash = function(hash,queue){
1078
+ // Handle Queueing
1079
+ if ( queue !== false && History.busy() ) {
1080
+ // Wait + Push to Queue
1081
+ //History.debug('History.setHash: we must wait', arguments);
1082
+ History.pushQueue({
1083
+ scope: History,
1084
+ callback: History.setHash,
1085
+ args: arguments,
1086
+ queue: queue
1087
+ });
1088
+ return false;
1089
+ }
1090
+
1091
+ // Log
1092
+ //History.debug('History.setHash: called',hash);
1093
+
1094
+ // Prepare
1095
+ var adjustedHash = History.escapeHash(hash);
1096
+
1097
+ // Make Busy + Continue
1098
+ History.busy(true);
1099
+
1100
+ // Check if hash is a state
1101
+ var State = History.extractState(hash,true);
1102
+ if ( State && !History.emulated.pushState ) {
1103
+ // Hash is a state so skip the setHash
1104
+ //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
1105
+
1106
+ // PushState
1107
+ History.pushState(State.data,State.title,State.url,false);
1108
+ }
1109
+ else if ( document.location.hash !== adjustedHash ) {
1110
+ // Hash is a proper hash, so apply it
1111
+
1112
+ // Handle browser bugs
1113
+ if ( History.bugs.setHash ) {
1114
+ // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
1115
+
1116
+ // Fetch the base page
1117
+ var pageUrl = History.getPageUrl();
1118
+
1119
+ // Safari hash apply
1120
+ History.pushState(null,null,pageUrl+'#'+adjustedHash,false);
1121
+ }
1122
+ else {
1123
+ // Normal hash apply
1124
+ document.location.hash = adjustedHash;
1125
+ }
1126
+ }
1127
+
1128
+ // Chain
1129
+ return History;
1130
+ };
1131
+
1132
+ /**
1133
+ * History.escape()
1134
+ * normalize and Escape a Hash
1135
+ * @return {string}
1136
+ */
1137
+ History.escapeHash = function(hash){
1138
+ var result = History.normalizeHash(hash);
1139
+
1140
+ // Escape hash
1141
+ result = window.escape(result);
1142
+
1143
+ // IE6 Escape Bug
1144
+ if ( !History.bugs.hashEscape ) {
1145
+ // Restore common parts
1146
+ result = result
1147
+ .replace(/\%21/g,'!')
1148
+ .replace(/\%26/g,'&')
1149
+ .replace(/\%3D/g,'=')
1150
+ .replace(/\%3F/g,'?');
1151
+ }
1152
+
1153
+ // Return result
1154
+ return result;
1155
+ };
1156
+
1157
+ /**
1158
+ * History.getHashByUrl(url)
1159
+ * Extracts the Hash from a URL
1160
+ * @param {string} url
1161
+ * @return {string} url
1162
+ */
1163
+ History.getHashByUrl = function(url){
1164
+ // Extract the hash
1165
+ var hash = String(url)
1166
+ .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
1167
+ ;
1168
+
1169
+ // Unescape hash
1170
+ hash = History.unescapeHash(hash);
1171
+
1172
+ // Return hash
1173
+ return hash;
1174
+ };
1175
+
1176
+ /**
1177
+ * History.setTitle(title)
1178
+ * Applies the title to the document
1179
+ * @param {State} newState
1180
+ * @return {Boolean}
1181
+ */
1182
+ History.setTitle = function(newState){
1183
+ // Prepare
1184
+ var title = newState.title;
1185
+
1186
+ // Initial
1187
+ if ( !title ) {
1188
+ var firstState = History.getStateByIndex(0);
1189
+ if ( firstState && firstState.url === newState.url ) {
1190
+ title = firstState.title||History.options.initialTitle;
1191
+ }
1192
+ }
1193
+
1194
+ // Apply
1195
+ try {
1196
+ document.getElementsByTagName('title')[0].innerHTML = title.replace('<','&lt;').replace('>','&gt;').replace(' & ',' &amp; ');
1197
+ }
1198
+ catch ( Exception ) { }
1199
+ document.title = title;
1200
+
1201
+ // Chain
1202
+ return History;
1203
+ };
1204
+
1205
+ // ----------------------------------------------------------------------
1206
+ // Queueing
1207
+
1208
+ /**
1209
+ * History.queues
1210
+ * The list of queues to use
1211
+ * First In, First Out
1212
+ */
1213
+ History.queues = [];
1214
+
1215
+ /**
1216
+ * History.busy(value)
1217
+ * @param {boolean} value [optional]
1218
+ * @return {boolean} busy
1219
+ */
1220
+ History.busy = function(value){
1221
+ // Apply
1222
+ if ( typeof value !== 'undefined' ) {
1223
+ //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length);
1224
+ History.busy.flag = value;
1225
+ }
1226
+ // Default
1227
+ else if ( typeof History.busy.flag === 'undefined' ) {
1228
+ History.busy.flag = false;
1229
+ }
1230
+
1231
+ // Queue
1232
+ if ( !History.busy.flag ) {
1233
+ // Execute the next item in the queue
1234
+ clearTimeout(History.busy.timeout);
1235
+ var fireNext = function(){
1236
+ if ( History.busy.flag ) return;
1237
+ for ( var i=History.queues.length-1; i >= 0; --i ) {
1238
+ var queue = History.queues[i];
1239
+ if ( queue.length === 0 ) continue;
1240
+ var item = queue.shift();
1241
+ History.fireQueueItem(item);
1242
+ History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1243
+ }
1244
+ };
1245
+ History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1246
+ }
1247
+
1248
+ // Return
1249
+ return History.busy.flag;
1250
+ };
1251
+
1252
+ /**
1253
+ * History.fireQueueItem(item)
1254
+ * Fire a Queue Item
1255
+ * @param {Object} item
1256
+ * @return {Mixed} result
1257
+ */
1258
+ History.fireQueueItem = function(item){
1259
+ return item.callback.apply(item.scope||History,item.args||[]);
1260
+ };
1261
+
1262
+ /**
1263
+ * History.pushQueue(callback,args)
1264
+ * Add an item to the queue
1265
+ * @param {Object} item [scope,callback,args,queue]
1266
+ */
1267
+ History.pushQueue = function(item){
1268
+ // Prepare the queue
1269
+ History.queues[item.queue||0] = History.queues[item.queue||0]||[];
1270
+
1271
+ // Add to the queue
1272
+ History.queues[item.queue||0].push(item);
1273
+
1274
+ // Chain
1275
+ return History;
1276
+ };
1277
+
1278
+ /**
1279
+ * History.queue (item,queue), (func,queue), (func), (item)
1280
+ * Either firs the item now if not busy, or adds it to the queue
1281
+ */
1282
+ History.queue = function(item,queue){
1283
+ // Prepare
1284
+ if ( typeof item === 'function' ) {
1285
+ item = {
1286
+ callback: item
1287
+ };
1288
+ }
1289
+ if ( typeof queue !== 'undefined' ) {
1290
+ item.queue = queue;
1291
+ }
1292
+
1293
+ // Handle
1294
+ if ( History.busy() ) {
1295
+ History.pushQueue(item);
1296
+ } else {
1297
+ History.fireQueueItem(item);
1298
+ }
1299
+
1300
+ // Chain
1301
+ return History;
1302
+ };
1303
+
1304
+ /**
1305
+ * History.clearQueue()
1306
+ * Clears the Queue
1307
+ */
1308
+ History.clearQueue = function(){
1309
+ History.busy.flag = false;
1310
+ History.queues = [];
1311
+ return History;
1312
+ };
1313
+
1314
+
1315
+ // ----------------------------------------------------------------------
1316
+ // IE Bug Fix
1317
+
1318
+ /**
1319
+ * History.stateChanged
1320
+ * States whether or not the state has changed since the last double check was initialised
1321
+ */
1322
+ History.stateChanged = false;
1323
+
1324
+ /**
1325
+ * History.doubleChecker
1326
+ * Contains the timeout used for the double checks
1327
+ */
1328
+ History.doubleChecker = false;
1329
+
1330
+ /**
1331
+ * History.doubleCheckComplete()
1332
+ * Complete a double check
1333
+ * @return {History}
1334
+ */
1335
+ History.doubleCheckComplete = function(){
1336
+ // Update
1337
+ History.stateChanged = true;
1338
+
1339
+ // Clear
1340
+ History.doubleCheckClear();
1341
+
1342
+ // Chain
1343
+ return History;
1344
+ };
1345
+
1346
+ /**
1347
+ * History.doubleCheckClear()
1348
+ * Clear a double check
1349
+ * @return {History}
1350
+ */
1351
+ History.doubleCheckClear = function(){
1352
+ // Clear
1353
+ if ( History.doubleChecker ) {
1354
+ clearTimeout(History.doubleChecker);
1355
+ History.doubleChecker = false;
1356
+ }
1357
+
1358
+ // Chain
1359
+ return History;
1360
+ };
1361
+
1362
+ /**
1363
+ * History.doubleCheck()
1364
+ * Create a double check
1365
+ * @return {History}
1366
+ */
1367
+ History.doubleCheck = function(tryAgain){
1368
+ // Reset
1369
+ History.stateChanged = false;
1370
+ History.doubleCheckClear();
1371
+
1372
+ // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)
1373
+ // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940
1374
+ if ( History.bugs.ieDoubleCheck ) {
1375
+ // Apply Check
1376
+ History.doubleChecker = setTimeout(
1377
+ function(){
1378
+ History.doubleCheckClear();
1379
+ if ( !History.stateChanged ) {
1380
+ //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
1381
+ // Re-Attempt
1382
+ tryAgain();
1383
+ }
1384
+ return true;
1385
+ },
1386
+ History.options.doubleCheckInterval
1387
+ );
1388
+ }
1389
+
1390
+ // Chain
1391
+ return History;
1392
+ };
1393
+
1394
+ // ----------------------------------------------------------------------
1395
+ // Safari Bug Fix
1396
+
1397
+ /**
1398
+ * History.safariStatePoll()
1399
+ * Poll the current state
1400
+ * @return {History}
1401
+ */
1402
+ History.safariStatePoll = function(){
1403
+ // Poll the URL
1404
+
1405
+ // Get the Last State which has the new URL
1406
+ var
1407
+ urlState = History.extractState(document.location.href),
1408
+ newState;
1409
+
1410
+ // Check for a difference
1411
+ if ( !History.isLastSavedState(urlState) ) {
1412
+ newState = urlState;
1413
+ }
1414
+ else {
1415
+ return;
1416
+ }
1417
+
1418
+ // Check if we have a state with that url
1419
+ // If not create it
1420
+ if ( !newState ) {
1421
+ //History.debug('History.safariStatePoll: new');
1422
+ newState = History.createStateObject();
1423
+ }
1424
+
1425
+ // Apply the New State
1426
+ //History.debug('History.safariStatePoll: trigger');
1427
+ History.Adapter.trigger(window,'popstate');
1428
+
1429
+ // Chain
1430
+ return History;
1431
+ };
1432
+
1433
+ // ----------------------------------------------------------------------
1434
+ // State Aliases
1435
+
1436
+ /**
1437
+ * History.back(queue)
1438
+ * Send the browser history back one item
1439
+ * @param {Integer} queue [optional]
1440
+ */
1441
+ History.back = function(queue){
1442
+ //History.debug('History.back: called', arguments);
1443
+
1444
+ // Handle Queueing
1445
+ if ( queue !== false && History.busy() ) {
1446
+ // Wait + Push to Queue
1447
+ //History.debug('History.back: we must wait', arguments);
1448
+ History.pushQueue({
1449
+ scope: History,
1450
+ callback: History.back,
1451
+ args: arguments,
1452
+ queue: queue
1453
+ });
1454
+ return false;
1455
+ }
1456
+
1457
+ // Make Busy + Continue
1458
+ History.busy(true);
1459
+
1460
+ // Fix certain browser bugs that prevent the state from changing
1461
+ History.doubleCheck(function(){
1462
+ History.back(false);
1463
+ });
1464
+
1465
+ // Go back
1466
+ history.go(-1);
1467
+
1468
+ // End back closure
1469
+ return true;
1470
+ };
1471
+
1472
+ /**
1473
+ * History.forward(queue)
1474
+ * Send the browser history forward one item
1475
+ * @param {Integer} queue [optional]
1476
+ */
1477
+ History.forward = function(queue){
1478
+ //History.debug('History.forward: called', arguments);
1479
+
1480
+ // Handle Queueing
1481
+ if ( queue !== false && History.busy() ) {
1482
+ // Wait + Push to Queue
1483
+ //History.debug('History.forward: we must wait', arguments);
1484
+ History.pushQueue({
1485
+ scope: History,
1486
+ callback: History.forward,
1487
+ args: arguments,
1488
+ queue: queue
1489
+ });
1490
+ return false;
1491
+ }
1492
+
1493
+ // Make Busy + Continue
1494
+ History.busy(true);
1495
+
1496
+ // Fix certain browser bugs that prevent the state from changing
1497
+ History.doubleCheck(function(){
1498
+ History.forward(false);
1499
+ });
1500
+
1501
+ // Go forward
1502
+ history.go(1);
1503
+
1504
+ // End forward closure
1505
+ return true;
1506
+ };
1507
+
1508
+ /**
1509
+ * History.go(index,queue)
1510
+ * Send the browser history back or forward index times
1511
+ * @param {Integer} queue [optional]
1512
+ */
1513
+ History.go = function(index,queue){
1514
+ //History.debug('History.go: called', arguments);
1515
+
1516
+ // Prepare
1517
+ var i;
1518
+
1519
+ // Handle
1520
+ if ( index > 0 ) {
1521
+ // Forward
1522
+ for ( i=1; i<=index; ++i ) {
1523
+ History.forward(queue);
1524
+ }
1525
+ }
1526
+ else if ( index < 0 ) {
1527
+ // Backward
1528
+ for ( i=-1; i>=index; --i ) {
1529
+ History.back(queue);
1530
+ }
1531
+ }
1532
+ else {
1533
+ throw new Error('History.go: History.go requires a positive or negative integer passed.');
1534
+ }
1535
+
1536
+ // Chain
1537
+ return History;
1538
+ };
1539
+
1540
+
1541
+ // ----------------------------------------------------------------------
1542
+ // Initialise
1543
+
1544
+ /**
1545
+ * Create the initial State
1546
+ */
1547
+ History.saveState(History.storeState(History.extractState(document.location.href,true)));
1548
+
1549
+ /**
1550
+ * Bind for Saving Store
1551
+ */
1552
+ if ( amplify ) {
1553
+ History.onUnload = function(){
1554
+ // Prepare
1555
+ var
1556
+ currentStore = amplify.store('History.store')||{},
1557
+ item;
1558
+
1559
+ // Ensure
1560
+ currentStore.idToState = currentStore.idToState || {};
1561
+ currentStore.urlToId = currentStore.urlToId || {};
1562
+ currentStore.stateToId = currentStore.stateToId || {};
1563
+
1564
+ // Sync
1565
+ for ( item in History.idToState ) {
1566
+ if ( !History.idToState.hasOwnProperty(item) ) {
1567
+ continue;
1568
+ }
1569
+ currentStore.idToState[item] = History.idToState[item];
1570
+ }
1571
+ for ( item in History.urlToId ) {
1572
+ if ( !History.urlToId.hasOwnProperty(item) ) {
1573
+ continue;
1574
+ }
1575
+ currentStore.urlToId[item] = History.urlToId[item];
1576
+ }
1577
+ for ( item in History.stateToId ) {
1578
+ if ( !History.stateToId.hasOwnProperty(item) ) {
1579
+ continue;
1580
+ }
1581
+ currentStore.stateToId[item] = History.stateToId[item];
1582
+ }
1583
+
1584
+ // Update
1585
+ History.store = currentStore;
1586
+
1587
+ // Store
1588
+ amplify.store('History.store',currentStore);
1589
+ };
1590
+ // For Internet Explorer
1591
+ History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval));
1592
+ // For Other Browsers
1593
+ History.Adapter.bind(window,'beforeunload',History.onUnload);
1594
+ History.Adapter.bind(window,'unload',History.onUnload);
1595
+ // Both are enabled for consistency
1596
+ }
1597
+
1598
+
1599
+ // ----------------------------------------------------------------------
1600
+ // HTML5 State Support
1601
+
1602
+ if ( History.emulated.pushState ) {
1603
+ /*
1604
+ * Provide Skeleton for HTML4 Browsers
1605
+ */
1606
+
1607
+ // Prepare
1608
+ var emptyFunction = function(){};
1609
+ History.pushState = History.pushState||emptyFunction;
1610
+ History.replaceState = History.replaceState||emptyFunction;
1611
+ }
1612
+ else {
1613
+ /*
1614
+ * Use native HTML5 History API Implementation
1615
+ */
1616
+
1617
+ /**
1618
+ * History.onPopState(event,extra)
1619
+ * Refresh the Current State
1620
+ */
1621
+ History.onPopState = function(event){
1622
+ // Reset the double check
1623
+ History.doubleCheckComplete();
1624
+
1625
+ // Check for a Hash, and handle apporiatly
1626
+ var currentHash = History.getHash();
1627
+ if ( currentHash ) {
1628
+ // Expand Hash
1629
+ var currentState = History.extractState(currentHash||document.location.href,true);
1630
+ if ( currentState ) {
1631
+ // We were able to parse it, it must be a State!
1632
+ // Let's forward to replaceState
1633
+ //History.debug('History.onPopState: state anchor', currentHash, currentState);
1634
+ History.replaceState(currentState.data, currentState.title, currentState.url, false);
1635
+ }
1636
+ else {
1637
+ // Traditional Anchor
1638
+ //History.debug('History.onPopState: traditional anchor', currentHash);
1639
+ History.Adapter.trigger(window,'anchorchange');
1640
+ History.busy(false);
1641
+ }
1642
+
1643
+ // We don't care for hashes
1644
+ History.expectedStateId = false;
1645
+ return false;
1646
+ }
1647
+
1648
+ // Prepare
1649
+ var newState = false;
1650
+
1651
+ // Prepare
1652
+ event = event||{};
1653
+ if ( typeof event.state === 'undefined' ) {
1654
+ // jQuery
1655
+ if ( typeof event.originalEvent !== 'undefined' && typeof event.originalEvent.state !== 'undefined' ) {
1656
+ event.state = event.originalEvent.state||false;
1657
+ }
1658
+ // MooTools
1659
+ else if ( typeof event.event !== 'undefined' && typeof event.event.state !== 'undefined' ) {
1660
+ event.state = event.event.state||false;
1661
+ }
1662
+ }
1663
+
1664
+ // Ensure
1665
+ event.state = (event.state||false);
1666
+
1667
+ // Fetch State
1668
+ if ( event.state ) {
1669
+ // Vanilla: Back/forward button was used
1670
+ newState = History.getStateById(event.state);
1671
+ }
1672
+ else if ( History.expectedStateId ) {
1673
+ // Vanilla: A new state was pushed, and popstate was called manually
1674
+ newState = History.getStateById(History.expectedStateId);
1675
+ }
1676
+ else {
1677
+ // Initial State
1678
+ newState = History.extractState(document.location.href);
1679
+ }
1680
+
1681
+ // The State did not exist in our store
1682
+ if ( !newState ) {
1683
+ // Regenerate the State
1684
+ newState = History.createStateObject(null,null,document.location.href);
1685
+ }
1686
+
1687
+ // Clean
1688
+ History.expectedStateId = false;
1689
+
1690
+ // Check if we are the same state
1691
+ if ( History.isLastSavedState(newState) ) {
1692
+ // There has been no change (just the page's hash has finally propagated)
1693
+ //History.debug('History.onPopState: no change', newState, History.savedStates);
1694
+ History.busy(false);
1695
+ return false;
1696
+ }
1697
+
1698
+ // Store the State
1699
+ History.storeState(newState);
1700
+ History.saveState(newState);
1701
+
1702
+ // Force update of the title
1703
+ History.setTitle(newState);
1704
+
1705
+ // Fire Our Event
1706
+ History.Adapter.trigger(window,'statechange');
1707
+ History.busy(false);
1708
+
1709
+ // Return true
1710
+ return true;
1711
+ };
1712
+ History.Adapter.bind(window,'popstate',History.onPopState);
1713
+
1714
+ /**
1715
+ * History.pushState(data,title,url)
1716
+ * Add a new State to the history object, become it, and trigger onpopstate
1717
+ * We have to trigger for HTML4 compatibility
1718
+ * @param {object} data
1719
+ * @param {string} title
1720
+ * @param {string} url
1721
+ * @return {true}
1722
+ */
1723
+ History.pushState = function(data,title,url,queue){
1724
+ //History.debug('History.pushState: called', arguments);
1725
+
1726
+ // Check the State
1727
+ if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1728
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1729
+ }
1730
+
1731
+ // Handle Queueing
1732
+ if ( queue !== false && History.busy() ) {
1733
+ // Wait + Push to Queue
1734
+ //History.debug('History.pushState: we must wait', arguments);
1735
+ History.pushQueue({
1736
+ scope: History,
1737
+ callback: History.pushState,
1738
+ args: arguments,
1739
+ queue: queue
1740
+ });
1741
+ return false;
1742
+ }
1743
+
1744
+ // Make Busy + Continue
1745
+ History.busy(true);
1746
+
1747
+ // Create the newState
1748
+ var newState = History.createStateObject(data,title,url);
1749
+
1750
+ // Check it
1751
+ if ( History.isLastSavedState(newState) ) {
1752
+ // Won't be a change
1753
+ History.busy(false);
1754
+ }
1755
+ else {
1756
+ // Store the newState
1757
+ History.storeState(newState);
1758
+ History.expectedStateId = newState.id;
1759
+
1760
+ // Push the newState
1761
+ history.pushState(newState.id,newState.title,newState.url);
1762
+
1763
+ // Fire HTML5 Event
1764
+ History.Adapter.trigger(window,'popstate');
1765
+ }
1766
+
1767
+ // End pushState closure
1768
+ return true;
1769
+ };
1770
+
1771
+ /**
1772
+ * History.replaceState(data,title,url)
1773
+ * Replace the State and trigger onpopstate
1774
+ * We have to trigger for HTML4 compatibility
1775
+ * @param {object} data
1776
+ * @param {string} title
1777
+ * @param {string} url
1778
+ * @return {true}
1779
+ */
1780
+ History.replaceState = function(data,title,url,queue){
1781
+ //History.debug('History.replaceState: called', arguments);
1782
+
1783
+ // Check the State
1784
+ if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1785
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1786
+ }
1787
+
1788
+ // Handle Queueing
1789
+ if ( queue !== false && History.busy() ) {
1790
+ // Wait + Push to Queue
1791
+ //History.debug('History.replaceState: we must wait', arguments);
1792
+ History.pushQueue({
1793
+ scope: History,
1794
+ callback: History.replaceState,
1795
+ args: arguments,
1796
+ queue: queue
1797
+ });
1798
+ return false;
1799
+ }
1800
+
1801
+ // Make Busy + Continue
1802
+ History.busy(true);
1803
+
1804
+ // Create the newState
1805
+ var newState = History.createStateObject(data,title,url);
1806
+
1807
+ // Check it
1808
+ if ( History.isLastSavedState(newState) ) {
1809
+ // Won't be a change
1810
+ History.busy(false);
1811
+ }
1812
+ else {
1813
+ // Store the newState
1814
+ History.storeState(newState);
1815
+ History.expectedStateId = newState.id;
1816
+
1817
+ // Push the newState
1818
+ history.replaceState(newState.id,newState.title,newState.url);
1819
+
1820
+ // Fire HTML5 Event
1821
+ History.Adapter.trigger(window,'popstate');
1822
+ }
1823
+
1824
+ // End replaceState closure
1825
+ return true;
1826
+ };
1827
+
1828
+ // Be aware, the following is only for native pushState implementations
1829
+ // If you are wanting to include something for all browsers
1830
+ // Then include it above this if block
1831
+
1832
+ /**
1833
+ * Setup Safari Fix
1834
+ */
1835
+ if ( History.bugs.safariPoll ) {
1836
+ History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval));
1837
+ }
1838
+
1839
+ /**
1840
+ * Ensure Cross Browser Compatibility
1841
+ */
1842
+ if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) {
1843
+ /**
1844
+ * Fix Safari HashChange Issue
1845
+ */
1846
+
1847
+ // Setup Alias
1848
+ History.Adapter.bind(window,'hashchange',function(){
1849
+ History.Adapter.trigger(window,'popstate');
1850
+ });
1851
+
1852
+ // Initialise Alias
1853
+ if ( History.getHash() ) {
1854
+ History.Adapter.onDomLoad(function(){
1855
+ History.Adapter.trigger(window,'hashchange');
1856
+ });
1857
+ }
1858
+ }
1859
+
1860
+ } // !History.emulated.pushState
1861
+
1862
+ }; // History.initCore
1863
+
1864
+ // Try and Initialise History
1865
+ History.init();
1866
+ })(window, jQuery, Object);
js/jquery/jquery-ui.js ADDED
@@ -0,0 +1,11827 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery UI 1.8.17
3
+ *
4
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://docs.jquery.com/UI
9
+ */
10
+ (function( $, undefined ) {
11
+
12
+ // prevent duplicate loading
13
+ // this is only a problem because we proxy existing functions
14
+ // and we don't want to double proxy them
15
+ $.ui = $.ui || {};
16
+ if ( $.ui.version ) {
17
+ return;
18
+ }
19
+
20
+ $.extend( $.ui, {
21
+ version: "1.8.17",
22
+
23
+ keyCode: {
24
+ ALT: 18,
25
+ BACKSPACE: 8,
26
+ CAPS_LOCK: 20,
27
+ COMMA: 188,
28
+ COMMAND: 91,
29
+ COMMAND_LEFT: 91, // COMMAND
30
+ COMMAND_RIGHT: 93,
31
+ CONTROL: 17,
32
+ DELETE: 46,
33
+ DOWN: 40,
34
+ END: 35,
35
+ ENTER: 13,
36
+ ESCAPE: 27,
37
+ HOME: 36,
38
+ INSERT: 45,
39
+ LEFT: 37,
40
+ MENU: 93, // COMMAND_RIGHT
41
+ NUMPAD_ADD: 107,
42
+ NUMPAD_DECIMAL: 110,
43
+ NUMPAD_DIVIDE: 111,
44
+ NUMPAD_ENTER: 108,
45
+ NUMPAD_MULTIPLY: 106,
46
+ NUMPAD_SUBTRACT: 109,
47
+ PAGE_DOWN: 34,
48
+ PAGE_UP: 33,
49
+ PERIOD: 190,
50
+ RIGHT: 39,
51
+ SHIFT: 16,
52
+ SPACE: 32,
53
+ TAB: 9,
54
+ UP: 38,
55
+ WINDOWS: 91 // COMMAND
56
+ }
57
+ });
58
+
59
+ // plugins
60
+ $.fn.extend({
61
+ propAttr: $.fn.prop || $.fn.attr,
62
+
63
+ _focus: $.fn.focus,
64
+ focus: function( delay, fn ) {
65
+ return typeof delay === "number" ?
66
+ this.each(function() {
67
+ var elem = this;
68
+ setTimeout(function() {
69
+ $( elem ).focus();
70
+ if ( fn ) {
71
+ fn.call( elem );
72
+ }
73
+ }, delay );
74
+ }) :
75
+ this._focus.apply( this, arguments );
76
+ },
77
+
78
+ scrollParent: function() {
79
+ var scrollParent;
80
+ if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
81
+ scrollParent = this.parents().filter(function() {
82
+ return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
83
+ }).eq(0);
84
+ } else {
85
+ scrollParent = this.parents().filter(function() {
86
+ return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
87
+ }).eq(0);
88
+ }
89
+
90
+ return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
91
+ },
92
+
93
+ zIndex: function( zIndex ) {
94
+ if ( zIndex !== undefined ) {
95
+ return this.css( "zIndex", zIndex );
96
+ }
97
+
98
+ if ( this.length ) {
99
+ var elem = $( this[ 0 ] ), position, value;
100
+ while ( elem.length && elem[ 0 ] !== document ) {
101
+ // Ignore z-index if position is set to a value where z-index is ignored by the browser
102
+ // This makes behavior of this function consistent across browsers
103
+ // WebKit always returns auto if the element is positioned
104
+ position = elem.css( "position" );
105
+ if ( position === "absolute" || position === "relative" || position === "fixed" ) {
106
+ // IE returns 0 when zIndex is not specified
107
+ // other browsers return a string
108
+ // we ignore the case of nested elements with an explicit value of 0
109
+ // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
110
+ value = parseInt( elem.css( "zIndex" ), 10 );
111
+ if ( !isNaN( value ) && value !== 0 ) {
112
+ return value;
113
+ }
114
+ }
115
+ elem = elem.parent();
116
+ }
117
+ }
118
+
119
+ return 0;
120
+ },
121
+
122
+ disableSelection: function() {
123
+ return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
124
+ ".ui-disableSelection", function( event ) {
125
+ event.preventDefault();
126
+ });
127
+ },
128
+
129
+ enableSelection: function() {
130
+ return this.unbind( ".ui-disableSelection" );
131
+ }
132
+ });
133
+
134
+ $.each( [ "Width", "Height" ], function( i, name ) {
135
+ var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
136
+ type = name.toLowerCase(),
137
+ orig = {
138
+ innerWidth: $.fn.innerWidth,
139
+ innerHeight: $.fn.innerHeight,
140
+ outerWidth: $.fn.outerWidth,
141
+ outerHeight: $.fn.outerHeight
142
+ };
143
+
144
+ function reduce( elem, size, border, margin ) {
145
+ $.each( side, function() {
146
+ size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
147
+ if ( border ) {
148
+ size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
149
+ }
150
+ if ( margin ) {
151
+ size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
152
+ }
153
+ });
154
+ return size;
155
+ }
156
+
157
+ $.fn[ "inner" + name ] = function( size ) {
158
+ if ( size === undefined ) {
159
+ return orig[ "inner" + name ].call( this );
160
+ }
161
+
162
+ return this.each(function() {
163
+ $( this ).css( type, reduce( this, size ) + "px" );
164
+ });
165
+ };
166
+
167
+ $.fn[ "outer" + name] = function( size, margin ) {
168
+ if ( typeof size !== "number" ) {
169
+ return orig[ "outer" + name ].call( this, size );
170
+ }
171
+
172
+ return this.each(function() {
173
+ $( this).css( type, reduce( this, size, true, margin ) + "px" );
174
+ });
175
+ };
176
+ });
177
+
178
+ // selectors
179
+ function focusable( element, isTabIndexNotNaN ) {
180
+ var nodeName = element.nodeName.toLowerCase();
181
+ if ( "area" === nodeName ) {
182
+ var map = element.parentNode,
183
+ mapName = map.name,
184
+ img;
185
+ if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
186
+ return false;
187
+ }
188
+ img = $( "img[usemap=#" + mapName + "]" )[0];
189
+ return !!img && visible( img );
190
+ }
191
+ return ( /input|select|textarea|button|object/.test( nodeName )
192
+ ? !element.disabled
193
+ : "a" == nodeName
194
+ ? element.href || isTabIndexNotNaN
195
+ : isTabIndexNotNaN)
196
+ // the element and all of its ancestors must be visible
197
+ && visible( element );
198
+ }
199
+
200
+ function visible( element ) {
201
+ return !$( element ).parents().andSelf().filter(function() {
202
+ return $.curCSS( this, "visibility" ) === "hidden" ||
203
+ $.expr.filters.hidden( this );
204
+ }).length;
205
+ }
206
+
207
+ $.extend( $.expr[ ":" ], {
208
+ data: function( elem, i, match ) {
209
+ return !!$.data( elem, match[ 3 ] );
210
+ },
211
+
212
+ focusable: function( element ) {
213
+ return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
214
+ },
215
+
216
+ tabbable: function( element ) {
217
+ var tabIndex = $.attr( element, "tabindex" ),
218
+ isTabIndexNaN = isNaN( tabIndex );
219
+ return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
220
+ }
221
+ });
222
+
223
+ // support
224
+ $(function() {
225
+ var body = document.body,
226
+ div = body.appendChild( div = document.createElement( "div" ) );
227
+
228
+ $.extend( div.style, {
229
+ minHeight: "100px",
230
+ height: "auto",
231
+ padding: 0,
232
+ borderWidth: 0
233
+ });
234
+
235
+ $.support.minHeight = div.offsetHeight === 100;
236
+ $.support.selectstart = "onselectstart" in div;
237
+
238
+ // set display to none to avoid a layout bug in IE
239
+ // http://dev.jquery.com/ticket/4014
240
+ body.removeChild( div ).style.display = "none";
241
+ });
242
+
243
+
244
+
245
+
246
+
247
+ // deprecated
248
+ $.extend( $.ui, {
249
+ // $.ui.plugin is deprecated. Use the proxy pattern instead.
250
+ plugin: {
251
+ add: function( module, option, set ) {
252
+ var proto = $.ui[ module ].prototype;
253
+ for ( var i in set ) {
254
+ proto.plugins[ i ] = proto.plugins[ i ] || [];
255
+ proto.plugins[ i ].push( [ option, set[ i ] ] );
256
+ }
257
+ },
258
+ call: function( instance, name, args ) {
259
+ var set = instance.plugins[ name ];
260
+ if ( !set || !instance.element[ 0 ].parentNode ) {
261
+ return;
262
+ }
263
+
264
+ for ( var i = 0; i < set.length; i++ ) {
265
+ if ( instance.options[ set[ i ][ 0 ] ] ) {
266
+ set[ i ][ 1 ].apply( instance.element, args );
267
+ }
268
+ }
269
+ }
270
+ },
271
+
272
+ // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
273
+ contains: function( a, b ) {
274
+ return document.compareDocumentPosition ?
275
+ a.compareDocumentPosition( b ) & 16 :
276
+ a !== b && a.contains( b );
277
+ },
278
+
279
+ // only used by resizable
280
+ hasScroll: function( el, a ) {
281
+
282
+ //If overflow is hidden, the element might have extra content, but the user wants to hide it
283
+ if ( $( el ).css( "overflow" ) === "hidden") {
284
+ return false;
285
+ }
286
+
287
+ var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
288
+ has = false;
289
+
290
+ if ( el[ scroll ] > 0 ) {
291
+ return true;
292
+ }
293
+
294
+ // TODO: determine which cases actually cause this to happen
295
+ // if the element doesn't have the scroll set, see if it's possible to
296
+ // set the scroll
297
+ el[ scroll ] = 1;
298
+ has = ( el[ scroll ] > 0 );
299
+ el[ scroll ] = 0;
300
+ return has;
301
+ },
302
+
303
+ // these are odd functions, fix the API or move into individual plugins
304
+ isOverAxis: function( x, reference, size ) {
305
+ //Determines when x coordinate is over "b" element axis
306
+ return ( x > reference ) && ( x < ( reference + size ) );
307
+ },
308
+ isOver: function( y, x, top, left, height, width ) {
309
+ //Determines when x, y coordinates is over "b" element
310
+ return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
311
+ }
312
+ });
313
+
314
+ })( jQuery );
315
+ /*!
316
+ * jQuery UI Widget 1.8.17
317
+ *
318
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
319
+ * Dual licensed under the MIT or GPL Version 2 licenses.
320
+ * http://jquery.org/license
321
+ *
322
+ * http://docs.jquery.com/UI/Widget
323
+ */
324
+ (function( $, undefined ) {
325
+
326
+ // jQuery 1.4+
327
+ if ( $.cleanData ) {
328
+ var _cleanData = $.cleanData;
329
+ $.cleanData = function( elems ) {
330
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
331
+ try {
332
+ $( elem ).triggerHandler( "remove" );
333
+ // http://bugs.jquery.com/ticket/8235
334
+ } catch( e ) {}
335
+ }
336
+ _cleanData( elems );
337
+ };
338
+ } else {
339
+ var _remove = $.fn.remove;
340
+ $.fn.remove = function( selector, keepData ) {
341
+ return this.each(function() {
342
+ if ( !keepData ) {
343
+ if ( !selector || $.filter( selector, [ this ] ).length ) {
344
+ $( "*", this ).add( [ this ] ).each(function() {
345
+ try {
346
+ $( this ).triggerHandler( "remove" );
347
+ // http://bugs.jquery.com/ticket/8235
348
+ } catch( e ) {}
349
+ });
350
+ }
351
+ }
352
+ return _remove.call( $(this), selector, keepData );
353
+ });
354
+ };
355
+ }
356
+
357
+ $.widget = function( name, base, prototype ) {
358
+ var namespace = name.split( "." )[ 0 ],
359
+ fullName;
360
+ name = name.split( "." )[ 1 ];
361
+ fullName = namespace + "-" + name;
362
+
363
+ if ( !prototype ) {
364
+ prototype = base;
365
+ base = $.Widget;
366
+ }
367
+
368
+ // create selector for plugin
369
+ $.expr[ ":" ][ fullName ] = function( elem ) {
370
+ return !!$.data( elem, name );
371
+ };
372
+
373
+ $[ namespace ] = $[ namespace ] || {};
374
+ $[ namespace ][ name ] = function( options, element ) {
375
+ // allow instantiation without initializing for simple inheritance
376
+ if ( arguments.length ) {
377
+ this._createWidget( options, element );
378
+ }
379
+ };
380
+
381
+ var basePrototype = new base();
382
+ // we need to make the options hash a property directly on the new instance
383
+ // otherwise we'll modify the options hash on the prototype that we're
384
+ // inheriting from
385
+ // $.each( basePrototype, function( key, val ) {
386
+ // if ( $.isPlainObject(val) ) {
387
+ // basePrototype[ key ] = $.extend( {}, val );
388
+ // }
389
+ // });
390
+ basePrototype.options = $.extend( true, {}, basePrototype.options );
391
+ $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
392
+ namespace: namespace,
393
+ widgetName: name,
394
+ widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
395
+ widgetBaseClass: fullName
396
+ }, prototype );
397
+
398
+ $.widget.bridge( name, $[ namespace ][ name ] );
399
+ };
400
+
401
+ $.widget.bridge = function( name, object ) {
402
+ $.fn[ name ] = function( options ) {
403
+ var isMethodCall = typeof options === "string",
404
+ args = Array.prototype.slice.call( arguments, 1 ),
405
+ returnValue = this;
406
+
407
+ // allow multiple hashes to be passed on init
408
+ options = !isMethodCall && args.length ?
409
+ $.extend.apply( null, [ true, options ].concat(args) ) :
410
+ options;
411
+
412
+ // prevent calls to internal methods
413
+ if ( isMethodCall && options.charAt( 0 ) === "_" ) {
414
+ return returnValue;
415
+ }
416
+
417
+ if ( isMethodCall ) {
418
+ this.each(function() {
419
+ var instance = $.data( this, name ),
420
+ methodValue = instance && $.isFunction( instance[options] ) ?
421
+ instance[ options ].apply( instance, args ) :
422
+ instance;
423
+ // TODO: add this back in 1.9 and use $.error() (see #5972)
424
+ // if ( !instance ) {
425
+ // throw "cannot call methods on " + name + " prior to initialization; " +
426
+ // "attempted to call method '" + options + "'";
427
+ // }
428
+ // if ( !$.isFunction( instance[options] ) ) {
429
+ // throw "no such method '" + options + "' for " + name + " widget instance";
430
+ // }
431
+ // var methodValue = instance[ options ].apply( instance, args );
432
+ if ( methodValue !== instance && methodValue !== undefined ) {
433
+ returnValue = methodValue;
434
+ return false;
435
+ }
436
+ });
437
+ } else {
438
+ this.each(function() {
439
+ var instance = $.data( this, name );
440
+ if ( instance ) {
441
+ instance.option( options || {} )._init();
442
+ } else {
443
+ $.data( this, name, new object( options, this ) );
444
+ }
445
+ });
446
+ }
447
+
448
+ return returnValue;
449
+ };
450
+ };
451
+
452
+ $.Widget = function( options, element ) {
453
+ // allow instantiation without initializing for simple inheritance
454
+ if ( arguments.length ) {
455
+ this._createWidget( options, element );
456
+ }
457
+ };
458
+
459
+ $.Widget.prototype = {
460
+ widgetName: "widget",
461
+ widgetEventPrefix: "",
462
+ options: {
463
+ disabled: false
464
+ },
465
+ _createWidget: function( options, element ) {
466
+ // $.widget.bridge stores the plugin instance, but we do it anyway
467
+ // so that it's stored even before the _create function runs
468
+ $.data( element, this.widgetName, this );
469
+ this.element = $( element );
470
+ this.options = $.extend( true, {},
471
+ this.options,
472
+ this._getCreateOptions(),
473
+ options );
474
+
475
+ var self = this;
476
+ this.element.bind( "remove." + this.widgetName, function() {
477
+ self.destroy();
478
+ });
479
+
480
+ this._create();
481
+ this._trigger( "create" );
482
+ this._init();
483
+ },
484
+ _getCreateOptions: function() {
485
+ return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
486
+ },
487
+ _create: function() {},
488
+ _init: function() {},
489
+
490
+ destroy: function() {
491
+ this.element
492
+ .unbind( "." + this.widgetName )
493
+ .removeData( this.widgetName );
494
+ this.widget()
495
+ .unbind( "." + this.widgetName )
496
+ .removeAttr( "aria-disabled" )
497
+ .removeClass(
498
+ this.widgetBaseClass + "-disabled " +
499
+ "ui-state-disabled" );
500
+ },
501
+
502
+ widget: function() {
503
+ return this.element;
504
+ },
505
+
506
+ option: function( key, value ) {
507
+ var options = key;
508
+
509
+ if ( arguments.length === 0 ) {
510
+ // don't return a reference to the internal hash
511
+ return $.extend( {}, this.options );
512
+ }
513
+
514
+ if (typeof key === "string" ) {
515
+ if ( value === undefined ) {
516
+ return this.options[ key ];
517
+ }
518
+ options = {};
519
+ options[ key ] = value;
520
+ }
521
+
522
+ this._setOptions( options );
523
+
524
+ return this;
525
+ },
526
+ _setOptions: function( options ) {
527
+ var self = this;
528
+ $.each( options, function( key, value ) {
529
+ self._setOption( key, value );
530
+ });
531
+
532
+ return this;
533
+ },
534
+ _setOption: function( key, value ) {
535
+ this.options[ key ] = value;
536
+
537
+ if ( key === "disabled" ) {
538
+ this.widget()
539
+ [ value ? "addClass" : "removeClass"](
540
+ this.widgetBaseClass + "-disabled" + " " +
541
+ "ui-state-disabled" )
542
+ .attr( "aria-disabled", value );
543
+ }
544
+
545
+ return this;
546
+ },
547
+
548
+ enable: function() {
549
+ return this._setOption( "disabled", false );
550
+ },
551
+ disable: function() {
552
+ return this._setOption( "disabled", true );
553
+ },
554
+
555
+ _trigger: function( type, event, data ) {
556
+ var prop, orig,
557
+ callback = this.options[ type ];
558
+
559
+ data = data || {};
560
+ event = $.Event( event );
561
+ event.type = ( type === this.widgetEventPrefix ?
562
+ type :
563
+ this.widgetEventPrefix + type ).toLowerCase();
564
+ // the original event may come from any element
565
+ // so we need to reset the target on the new event
566
+ event.target = this.element[ 0 ];
567
+
568
+ // copy original event properties over to the new event
569
+ orig = event.originalEvent;
570
+ if ( orig ) {
571
+ for ( prop in orig ) {
572
+ if ( !( prop in event ) ) {
573
+ event[ prop ] = orig[ prop ];
574
+ }
575
+ }
576
+ }
577
+
578
+ this.element.trigger( event, data );
579
+
580
+ return !( $.isFunction(callback) &&
581
+ callback.call( this.element[0], event, data ) === false ||
582
+ event.isDefaultPrevented() );
583
+ }
584
+ };
585
+
586
+ })( jQuery );
587
+ /*!
588
+ * jQuery UI Mouse 1.8.17
589
+ *
590
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
591
+ * Dual licensed under the MIT or GPL Version 2 licenses.
592
+ * http://jquery.org/license
593
+ *
594
+ * http://docs.jquery.com/UI/Mouse
595
+ *
596
+ * Depends:
597
+ * jquery.ui.widget.js
598
+ */
599
+ (function( $, undefined ) {
600
+
601
+ var mouseHandled = false;
602
+ $( document ).mouseup( function( e ) {
603
+ mouseHandled = false;
604
+ });
605
+
606
+ $.widget("ui.mouse", {
607
+ options: {
608
+ cancel: ':input,option',
609
+ distance: 1,
610
+ delay: 0
611
+ },
612
+ _mouseInit: function() {
613
+ var self = this;
614
+
615
+ this.element
616
+ .bind('mousedown.'+this.widgetName, function(event) {
617
+ return self._mouseDown(event);
618
+ })
619
+ .bind('click.'+this.widgetName, function(event) {
620
+ if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) {
621
+ $.removeData(event.target, self.widgetName + '.preventClickEvent');
622
+ event.stopImmediatePropagation();
623
+ return false;
624
+ }
625
+ });
626
+
627
+ this.started = false;
628
+ },
629
+
630
+ // TODO: make sure destroying one instance of mouse doesn't mess with
631
+ // other instances of mouse
632
+ _mouseDestroy: function() {
633
+ this.element.unbind('.'+this.widgetName);
634
+ },
635
+
636
+ _mouseDown: function(event) {
637
+ // don't let more than one widget handle mouseStart
638
+ if( mouseHandled ) { return };
639
+
640
+ // we may have missed mouseup (out of window)
641
+ (this._mouseStarted && this._mouseUp(event));
642
+
643
+ this._mouseDownEvent = event;
644
+
645
+ var self = this,
646
+ btnIsLeft = (event.which == 1),
647
+ // event.target.nodeName works around a bug in IE 8 with
648
+ // disabled inputs (#7620)
649
+ elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
650
+ if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
651
+ return true;
652
+ }
653
+
654
+ this.mouseDelayMet = !this.options.delay;
655
+ if (!this.mouseDelayMet) {
656
+ this._mouseDelayTimer = setTimeout(function() {
657
+ self.mouseDelayMet = true;
658
+ }, this.options.delay);
659
+ }
660
+
661
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
662
+ this._mouseStarted = (this._mouseStart(event) !== false);
663
+ if (!this._mouseStarted) {
664
+ event.preventDefault();
665
+ return true;
666
+ }
667
+ }
668
+
669
+ // Click event may never have fired (Gecko & Opera)
670
+ if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
671
+ $.removeData(event.target, this.widgetName + '.preventClickEvent');
672
+ }
673
+
674
+ // these delegates are required to keep context
675
+ this._mouseMoveDelegate = function(event) {
676
+ return self._mouseMove(event);
677
+ };
678
+ this._mouseUpDelegate = function(event) {
679
+ return self._mouseUp(event);
680
+ };
681
+ $(document)
682
+ .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
683
+ .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
684
+
685
+ event.preventDefault();
686
+
687
+ mouseHandled = true;
688
+ return true;
689
+ },
690
+
691
+ _mouseMove: function(event) {
692
+ // IE mouseup check - mouseup happened when mouse was out of window
693
+ if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
694
+ return this._mouseUp(event);
695
+ }
696
+
697
+ if (this._mouseStarted) {
698
+ this._mouseDrag(event);
699
+ return event.preventDefault();
700
+ }
701
+
702
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
703
+ this._mouseStarted =
704
+ (this._mouseStart(this._mouseDownEvent, event) !== false);
705
+ (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
706
+ }
707
+
708
+ return !this._mouseStarted;
709
+ },
710
+
711
+ _mouseUp: function(event) {
712
+ $(document)
713
+ .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
714
+ .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
715
+
716
+ if (this._mouseStarted) {
717
+ this._mouseStarted = false;
718
+
719
+ if (event.target == this._mouseDownEvent.target) {
720
+ $.data(event.target, this.widgetName + '.preventClickEvent', true);
721
+ }
722
+
723
+ this._mouseStop(event);
724
+ }
725
+
726
+ return false;
727
+ },
728
+
729
+ _mouseDistanceMet: function(event) {
730
+ return (Math.max(
731
+ Math.abs(this._mouseDownEvent.pageX - event.pageX),
732
+ Math.abs(this._mouseDownEvent.pageY - event.pageY)
733
+ ) >= this.options.distance
734
+ );
735
+ },
736
+
737
+ _mouseDelayMet: function(event) {
738
+ return this.mouseDelayMet;
739
+ },
740
+
741
+ // These are placeholder methods, to be overriden by extending plugin
742
+ _mouseStart: function(event) {},
743
+ _mouseDrag: function(event) {},
744
+ _mouseStop: function(event) {},
745
+ _mouseCapture: function(event) { return true; }
746
+ });
747
+
748
+ })(jQuery);
749
+ /*
750
+ * jQuery UI Position 1.8.17
751
+ *
752
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
753
+ * Dual licensed under the MIT or GPL Version 2 licenses.
754
+ * http://jquery.org/license
755
+ *
756
+ * http://docs.jquery.com/UI/Position
757
+ */
758
+ (function( $, undefined ) {
759
+
760
+ $.ui = $.ui || {};
761
+
762
+ var horizontalPositions = /left|center|right/,
763
+ verticalPositions = /top|center|bottom/,
764
+ center = "center",
765
+ support = {},
766
+ _position = $.fn.position,
767
+ _offset = $.fn.offset;
768
+
769
+ $.fn.position = function( options ) {
770
+ if ( !options || !options.of ) {
771
+ return _position.apply( this, arguments );
772
+ }
773
+
774
+ // make a copy, we don't want to modify arguments
775
+ options = $.extend( {}, options );
776
+
777
+ var target = $( options.of ),
778
+ targetElem = target[0],
779
+ collision = ( options.collision || "flip" ).split( " " ),
780
+ offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
781
+ targetWidth,
782
+ targetHeight,
783
+ basePosition;
784
+
785
+ if ( targetElem.nodeType === 9 ) {
786
+ targetWidth = target.width();
787
+ targetHeight = target.height();
788
+ basePosition = { top: 0, left: 0 };
789
+ // TODO: use $.isWindow() in 1.9
790
+ } else if ( targetElem.setTimeout ) {
791
+ targetWidth = target.width();
792
+ targetHeight = target.height();
793
+ basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
794
+ } else if ( targetElem.preventDefault ) {
795
+ // force left top to allow flipping
796
+ options.at = "left top";
797
+ targetWidth = targetHeight = 0;
798
+ basePosition = { top: options.of.pageY, left: options.of.pageX };
799
+ } else {
800
+ targetWidth = target.outerWidth();
801
+ targetHeight = target.outerHeight();
802
+ basePosition = target.offset();
803
+ }
804
+
805
+ // force my and at to have valid horizontal and veritcal positions
806
+ // if a value is missing or invalid, it will be converted to center
807
+ $.each( [ "my", "at" ], function() {
808
+ var pos = ( options[this] || "" ).split( " " );
809
+ if ( pos.length === 1) {
810
+ pos = horizontalPositions.test( pos[0] ) ?
811
+ pos.concat( [center] ) :
812
+ verticalPositions.test( pos[0] ) ?
813
+ [ center ].concat( pos ) :
814
+ [ center, center ];
815
+ }
816
+ pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
817
+ pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
818
+ options[ this ] = pos;
819
+ });
820
+
821
+ // normalize collision option
822
+ if ( collision.length === 1 ) {
823
+ collision[ 1 ] = collision[ 0 ];
824
+ }
825
+
826
+ // normalize offset option
827
+ offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
828
+ if ( offset.length === 1 ) {
829
+ offset[ 1 ] = offset[ 0 ];
830
+ }
831
+ offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
832
+
833
+ if ( options.at[0] === "right" ) {
834
+ basePosition.left += targetWidth;
835
+ } else if ( options.at[0] === center ) {
836
+ basePosition.left += targetWidth / 2;
837
+ }
838
+
839
+ if ( options.at[1] === "bottom" ) {
840
+ basePosition.top += targetHeight;
841
+ } else if ( options.at[1] === center ) {
842
+ basePosition.top += targetHeight / 2;
843
+ }
844
+
845
+ basePosition.left += offset[ 0 ];
846
+ basePosition.top += offset[ 1 ];
847
+
848
+ return this.each(function() {
849
+ var elem = $( this ),
850
+ elemWidth = elem.outerWidth(),
851
+ elemHeight = elem.outerHeight(),
852
+ marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
853
+ marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
854
+ collisionWidth = elemWidth + marginLeft +
855
+ ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ),
856
+ collisionHeight = elemHeight + marginTop +
857
+ ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ),
858
+ position = $.extend( {}, basePosition ),
859
+ collisionPosition;
860
+
861
+ if ( options.my[0] === "right" ) {
862
+ position.left -= elemWidth;
863
+ } else if ( options.my[0] === center ) {
864
+ position.left -= elemWidth / 2;
865
+ }
866
+
867
+ if ( options.my[1] === "bottom" ) {
868
+ position.top -= elemHeight;
869
+ } else if ( options.my[1] === center ) {
870
+ position.top -= elemHeight / 2;
871
+ }
872
+
873
+ // prevent fractions if jQuery version doesn't support them (see #5280)
874
+ if ( !support.fractions ) {
875
+ position.left = Math.round( position.left );
876
+ position.top = Math.round( position.top );
877
+ }
878
+
879
+ collisionPosition = {
880
+ left: position.left - marginLeft,
881
+ top: position.top - marginTop
882
+ };
883
+
884
+ $.each( [ "left", "top" ], function( i, dir ) {
885
+ if ( $.ui.position[ collision[i] ] ) {
886
+ $.ui.position[ collision[i] ][ dir ]( position, {
887
+ targetWidth: targetWidth,
888
+ targetHeight: targetHeight,
889
+ elemWidth: elemWidth,
890
+ elemHeight: elemHeight,
891
+ collisionPosition: collisionPosition,
892
+ collisionWidth: collisionWidth,
893
+ collisionHeight: collisionHeight,
894
+ offset: offset,
895
+ my: options.my,
896
+ at: options.at
897
+ });
898
+ }
899
+ });
900
+
901
+ if ( $.fn.bgiframe ) {
902
+ elem.bgiframe();
903
+ }
904
+ elem.offset( $.extend( position, { using: options.using } ) );
905
+ });
906
+ };
907
+
908
+ $.ui.position = {
909
+ fit: {
910
+ left: function( position, data ) {
911
+ var win = $( window ),
912
+ over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
913
+ position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
914
+ },
915
+ top: function( position, data ) {
916
+ var win = $( window ),
917
+ over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
918
+ position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
919
+ }
920
+ },
921
+
922
+ flip: {
923
+ left: function( position, data ) {
924
+ if ( data.at[0] === center ) {
925
+ return;
926
+ }
927
+ var win = $( window ),
928
+ over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
929
+ myOffset = data.my[ 0 ] === "left" ?
930
+ -data.elemWidth :
931
+ data.my[ 0 ] === "right" ?
932
+ data.elemWidth :
933
+ 0,
934
+ atOffset = data.at[ 0 ] === "left" ?
935
+ data.targetWidth :
936
+ -data.targetWidth,
937
+ offset = -2 * data.offset[ 0 ];
938
+ position.left += data.collisionPosition.left < 0 ?
939
+ myOffset + atOffset + offset :
940
+ over > 0 ?
941
+ myOffset + atOffset + offset :
942
+ 0;
943
+ },
944
+ top: function( position, data ) {
945
+ if ( data.at[1] === center ) {
946
+ return;
947
+ }
948
+ var win = $( window ),
949
+ over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
950
+ myOffset = data.my[ 1 ] === "top" ?
951
+ -data.elemHeight :
952
+ data.my[ 1 ] === "bottom" ?
953
+ data.elemHeight :
954
+ 0,
955
+ atOffset = data.at[ 1 ] === "top" ?
956
+ data.targetHeight :
957
+ -data.targetHeight,
958
+ offset = -2 * data.offset[ 1 ];
959
+ position.top += data.collisionPosition.top < 0 ?
960
+ myOffset + atOffset + offset :
961
+ over > 0 ?
962
+ myOffset + atOffset + offset :
963
+ 0;
964
+ }
965
+ }
966
+ };
967
+
968
+ // offset setter from jQuery 1.4
969
+ if ( !$.offset.setOffset ) {
970
+ $.offset.setOffset = function( elem, options ) {
971
+ // set position first, in-case top/left are set even on static elem
972
+ if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
973
+ elem.style.position = "relative";
974
+ }
975
+ var curElem = $( elem ),
976
+ curOffset = curElem.offset(),
977
+ curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0,
978
+ curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0,
979
+ props = {
980
+ top: (options.top - curOffset.top) + curTop,
981
+ left: (options.left - curOffset.left) + curLeft
982
+ };
983
+
984
+ if ( 'using' in options ) {
985
+ options.using.call( elem, props );
986
+ } else {
987
+ curElem.css( props );
988
+ }
989
+ };
990
+
991
+ $.fn.offset = function( options ) {
992
+ var elem = this[ 0 ];
993
+ if ( !elem || !elem.ownerDocument ) { return null; }
994
+ if ( options ) {
995
+ return this.each(function() {
996
+ $.offset.setOffset( this, options );
997
+ });
998
+ }
999
+ return _offset.call( this );
1000
+ };
1001
+ }
1002
+
1003
+ // fraction support test (older versions of jQuery don't support fractions)
1004
+ (function () {
1005
+ var body = document.getElementsByTagName( "body" )[ 0 ],
1006
+ div = document.createElement( "div" ),
1007
+ testElement, testElementParent, testElementStyle, offset, offsetTotal;
1008
+
1009
+ //Create a "fake body" for testing based on method used in jQuery.support
1010
+ testElement = document.createElement( body ? "div" : "body" );
1011
+ testElementStyle = {
1012
+ visibility: "hidden",
1013
+ width: 0,
1014
+ height: 0,
1015
+ border: 0,
1016
+ margin: 0,
1017
+ background: "none"
1018
+ };
1019
+ if ( body ) {
1020
+ jQuery.extend( testElementStyle, {
1021
+ position: "absolute",
1022
+ left: "-1000px",
1023
+ top: "-1000px"
1024
+ });
1025
+ }
1026
+ for ( var i in testElementStyle ) {
1027
+ testElement.style[ i ] = testElementStyle[ i ];
1028
+ }
1029
+ testElement.appendChild( div );
1030
+ testElementParent = body || document.documentElement;
1031
+ testElementParent.insertBefore( testElement, testElementParent.firstChild );
1032
+
1033
+ div.style.cssText = "position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;";
1034
+
1035
+ offset = $( div ).offset( function( _, offset ) {
1036
+ return offset;
1037
+ }).offset();
1038
+
1039
+ testElement.innerHTML = "";
1040
+ testElementParent.removeChild( testElement );
1041
+
1042
+ offsetTotal = offset.top + offset.left + ( body ? 2000 : 0 );
1043
+ support.fractions = offsetTotal > 21 && offsetTotal < 22;
1044
+ })();
1045
+
1046
+ }( jQuery ));
1047
+ /*
1048
+ * jQuery UI Draggable 1.8.17
1049
+ *
1050
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
1051
+ * Dual licensed under the MIT or GPL Version 2 licenses.
1052
+ * http://jquery.org/license
1053
+ *
1054
+ * http://docs.jquery.com/UI/Draggables
1055
+ *
1056
+ * Depends:
1057
+ * jquery.ui.core.js
1058
+ * jquery.ui.mouse.js
1059
+ * jquery.ui.widget.js
1060
+ */
1061
+ (function( $, undefined ) {
1062
+
1063
+ $.widget("ui.draggable", $.ui.mouse, {
1064
+ widgetEventPrefix: "drag",
1065
+ options: {
1066
+ addClasses: true,
1067
+ appendTo: "parent",
1068
+ axis: false,
1069
+ connectToSortable: false,
1070
+ containment: false,
1071
+ cursor: "auto",
1072
+ cursorAt: false,
1073
+ grid: false,
1074
+ handle: false,
1075
+ helper: "original",
1076
+ iframeFix: false,
1077
+ opacity: false,
1078
+ refreshPositions: false,
1079
+ revert: false,
1080
+ revertDuration: 500,
1081
+ scope: "default",
1082
+ scroll: true,
1083
+ scrollSensitivity: 20,
1084
+ scrollSpeed: 20,
1085
+ snap: false,
1086
+ snapMode: "both",
1087
+ snapTolerance: 20,
1088
+ stack: false,
1089
+ zIndex: false
1090
+ },
1091
+ _create: function() {
1092
+
1093
+ if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
1094
+ this.element[0].style.position = 'relative';
1095
+
1096
+ (this.options.addClasses && this.element.addClass("ui-draggable"));
1097
+ (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
1098
+
1099
+ this._mouseInit();
1100
+
1101
+ },
1102
+
1103
+ destroy: function() {
1104
+ if(!this.element.data('draggable')) return;
1105
+ this.element
1106
+ .removeData("draggable")
1107
+ .unbind(".draggable")
1108
+ .removeClass("ui-draggable"
1109
+ + " ui-draggable-dragging"
1110
+ + " ui-draggable-disabled");
1111
+ this._mouseDestroy();
1112
+
1113
+ return this;
1114
+ },
1115
+
1116
+ _mouseCapture: function(event) {
1117
+
1118
+ var o = this.options;
1119
+
1120
+ // among others, prevent a drag on a resizable-handle
1121
+ if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
1122
+ return false;
1123
+
1124
+ //Quit if we're not on a valid handle
1125
+ this.handle = this._getHandle(event);
1126
+ if (!this.handle)
1127
+ return false;
1128
+
1129
+ if ( o.iframeFix ) {
1130
+ $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1131
+ $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
1132
+ .css({
1133
+ width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1134
+ position: "absolute", opacity: "0.001", zIndex: 1000
1135
+ })
1136
+ .css($(this).offset())
1137
+ .appendTo("body");
1138
+ });
1139
+ }
1140
+
1141
+ return true;
1142
+
1143
+ },
1144
+
1145
+ _mouseStart: function(event) {
1146
+
1147
+ var o = this.options;
1148
+
1149
+ //Create and append the visible helper
1150
+ this.helper = this._createHelper(event);
1151
+
1152
+ //Cache the helper size
1153
+ this._cacheHelperProportions();
1154
+
1155
+ //If ddmanager is used for droppables, set the global draggable
1156
+ if($.ui.ddmanager)
1157
+ $.ui.ddmanager.current = this;
1158
+
1159
+ /*
1160
+ * - Position generation -
1161
+ * This block generates everything position related - it's the core of draggables.
1162
+ */
1163
+
1164
+ //Cache the margins of the original element
1165
+ this._cacheMargins();
1166
+
1167
+ //Store the helper's css position
1168
+ this.cssPosition = this.helper.css("position");
1169
+ this.scrollParent = this.helper.scrollParent();
1170
+
1171
+ //The element's absolute position on the page minus margins
1172
+ this.offset = this.positionAbs = this.element.offset();
1173
+ this.offset = {
1174
+ top: this.offset.top - this.margins.top,
1175
+ left: this.offset.left - this.margins.left
1176
+ };
1177
+
1178
+ $.extend(this.offset, {
1179
+ click: { //Where the click happened, relative to the element
1180
+ left: event.pageX - this.offset.left,
1181
+ top: event.pageY - this.offset.top
1182
+ },
1183
+ parent: this._getParentOffset(),
1184
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1185
+ });
1186
+
1187
+ //Generate the original position
1188
+ this.originalPosition = this.position = this._generatePosition(event);
1189
+ this.originalPageX = event.pageX;
1190
+ this.originalPageY = event.pageY;
1191
+
1192
+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
1193
+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1194
+
1195
+ //Set a containment if given in the options
1196
+ if(o.containment)
1197
+ this._setContainment();
1198
+
1199
+ //Trigger event + callbacks
1200
+ if(this._trigger("start", event) === false) {
1201
+ this._clear();
1202
+ return false;
1203
+ }
1204
+
1205
+ //Recache the helper size
1206
+ this._cacheHelperProportions();
1207
+
1208
+ //Prepare the droppable offsets
1209
+ if ($.ui.ddmanager && !o.dropBehaviour)
1210
+ $.ui.ddmanager.prepareOffsets(this, event);
1211
+
1212
+ this.helper.addClass("ui-draggable-dragging");
1213
+ this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1214
+
1215
+ //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1216
+ if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
1217
+
1218
+ return true;
1219
+ },
1220
+
1221
+ _mouseDrag: function(event, noPropagation) {
1222
+
1223
+ //Compute the helpers position
1224
+ this.position = this._generatePosition(event);
1225
+ this.positionAbs = this._convertPositionTo("absolute");
1226
+
1227
+ //Call plugins and callbacks and use the resulting position if something is returned
1228
+ if (!noPropagation) {
1229
+ var ui = this._uiHash();
1230
+ if(this._trigger('drag', event, ui) === false) {
1231
+ this._mouseUp({});
1232
+ return false;
1233
+ }
1234
+ this.position = ui.position;
1235
+ }
1236
+
1237
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
1238
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
1239
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
1240
+
1241
+ return false;
1242
+ },
1243
+
1244
+ _mouseStop: function(event) {
1245
+
1246
+ //If we are using droppables, inform the manager about the drop
1247
+ var dropped = false;
1248
+ if ($.ui.ddmanager && !this.options.dropBehaviour)
1249
+ dropped = $.ui.ddmanager.drop(this, event);
1250
+
1251
+ //if a drop comes from outside (a sortable)
1252
+ if(this.dropped) {
1253
+ dropped = this.dropped;
1254
+ this.dropped = false;
1255
+ }
1256
+
1257
+ //if the original element is removed, don't bother to continue if helper is set to "original"
1258
+ if((!this.element[0] || !this.element[0].parentNode) && this.options.helper == "original")
1259
+ return false;
1260
+
1261
+ if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
1262
+ var self = this;
1263
+ $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1264
+ if(self._trigger("stop", event) !== false) {
1265
+ self._clear();
1266
+ }
1267
+ });
1268
+ } else {
1269
+ if(this._trigger("stop", event) !== false) {
1270
+ this._clear();
1271
+ }
1272
+ }
1273
+
1274
+ return false;
1275
+ },
1276
+
1277
+ _mouseUp: function(event) {
1278
+ if (this.options.iframeFix === true) {
1279
+ $("div.ui-draggable-iframeFix").each(function() {
1280
+ this.parentNode.removeChild(this);
1281
+ }); //Remove frame helpers
1282
+ }
1283
+
1284
+ //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1285
+ if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
1286
+
1287
+ return $.ui.mouse.prototype._mouseUp.call(this, event);
1288
+ },
1289
+
1290
+ cancel: function() {
1291
+
1292
+ if(this.helper.is(".ui-draggable-dragging")) {
1293
+ this._mouseUp({});
1294
+ } else {
1295
+ this._clear();
1296
+ }
1297
+
1298
+ return this;
1299
+
1300
+ },
1301
+
1302
+ _getHandle: function(event) {
1303
+
1304
+ var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
1305
+ $(this.options.handle, this.element)
1306
+ .find("*")
1307
+ .andSelf()
1308
+ .each(function() {
1309
+ if(this == event.target) handle = true;
1310
+ });
1311
+
1312
+ return handle;
1313
+
1314
+ },
1315
+
1316
+ _createHelper: function(event) {
1317
+
1318
+ var o = this.options;
1319
+ var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
1320
+
1321
+ if(!helper.parents('body').length)
1322
+ helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
1323
+
1324
+ if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
1325
+ helper.css("position", "absolute");
1326
+
1327
+ return helper;
1328
+
1329
+ },
1330
+
1331
+ _adjustOffsetFromHelper: function(obj) {
1332
+ if (typeof obj == 'string') {
1333
+ obj = obj.split(' ');
1334
+ }
1335
+ if ($.isArray(obj)) {
1336
+ obj = {left: +obj[0], top: +obj[1] || 0};
1337
+ }
1338
+ if ('left' in obj) {
1339
+ this.offset.click.left = obj.left + this.margins.left;
1340
+ }
1341
+ if ('right' in obj) {
1342
+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1343
+ }
1344
+ if ('top' in obj) {
1345
+ this.offset.click.top = obj.top + this.margins.top;
1346
+ }
1347
+ if ('bottom' in obj) {
1348
+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1349
+ }
1350
+ },
1351
+
1352
+ _getParentOffset: function() {
1353
+
1354
+ //Get the offsetParent and cache its position
1355
+ this.offsetParent = this.helper.offsetParent();
1356
+ var po = this.offsetParent.offset();
1357
+
1358
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
1359
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1360
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1361
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1362
+ if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
1363
+ po.left += this.scrollParent.scrollLeft();
1364
+ po.top += this.scrollParent.scrollTop();
1365
+ }
1366
+
1367
+ if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
1368
+ || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
1369
+ po = { top: 0, left: 0 };
1370
+
1371
+ return {
1372
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1373
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1374
+ };
1375
+
1376
+ },
1377
+
1378
+ _getRelativeOffset: function() {
1379
+
1380
+ if(this.cssPosition == "relative") {
1381
+ var p = this.element.position();
1382
+ return {
1383
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1384
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1385
+ };
1386
+ } else {
1387
+ return { top: 0, left: 0 };
1388
+ }
1389
+
1390
+ },
1391
+
1392
+ _cacheMargins: function() {
1393
+ this.margins = {
1394
+ left: (parseInt(this.element.css("marginLeft"),10) || 0),
1395
+ top: (parseInt(this.element.css("marginTop"),10) || 0),
1396
+ right: (parseInt(this.element.css("marginRight"),10) || 0),
1397
+ bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1398
+ };
1399
+ },
1400
+
1401
+ _cacheHelperProportions: function() {
1402
+ this.helperProportions = {
1403
+ width: this.helper.outerWidth(),
1404
+ height: this.helper.outerHeight()
1405
+ };
1406
+ },
1407
+
1408
+ _setContainment: function() {
1409
+
1410
+ var o = this.options;
1411
+ if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
1412
+ if(o.containment == 'document' || o.containment == 'window') this.containment = [
1413
+ o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1414
+ o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1415
+ (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
1416
+ (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
1417
+ ];
1418
+
1419
+ if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
1420
+ var c = $(o.containment);
1421
+ var ce = c[0]; if(!ce) return;
1422
+ var co = c.offset();
1423
+ var over = ($(ce).css("overflow") != 'hidden');
1424
+
1425
+ this.containment = [
1426
+ (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
1427
+ (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
1428
+ (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
1429
+ (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
1430
+ ];
1431
+ this.relative_container = c;
1432
+
1433
+ } else if(o.containment.constructor == Array) {
1434
+ this.containment = o.containment;
1435
+ }
1436
+
1437
+ },
1438
+
1439
+ _convertPositionTo: function(d, pos) {
1440
+
1441
+ if(!pos) pos = this.position;
1442
+ var mod = d == "absolute" ? 1 : -1;
1443
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
1444
+
1445
+ return {
1446
+ top: (
1447
+ pos.top // The absolute mouse position
1448
+ + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1449
+ + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
1450
+ - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
1451
+ ),
1452
+ left: (
1453
+ pos.left // The absolute mouse position
1454
+ + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1455
+ + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
1456
+ - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
1457
+ )
1458
+ };
1459
+
1460
+ },
1461
+
1462
+ _generatePosition: function(event) {
1463
+
1464
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
1465
+ var pageX = event.pageX;
1466
+ var pageY = event.pageY;
1467
+
1468
+ /*
1469
+ * - Position constraining -
1470
+ * Constrain the position to a mix of grid, containment.
1471
+ */
1472
+
1473
+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
1474
+ var containment;
1475
+ if(this.containment) {
1476
+ if (this.relative_container){
1477
+ var co = this.relative_container.offset();
1478
+ containment = [ this.containment[0] + co.left,
1479
+ this.containment[1] + co.top,
1480
+ this.containment[2] + co.left,
1481
+ this.containment[3] + co.top ];
1482
+ }
1483
+ else {
1484
+ containment = this.containment;
1485
+ }
1486
+
1487
+ if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
1488
+ if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
1489
+ if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
1490
+ if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
1491
+ }
1492
+
1493
+ if(o.grid) {
1494
+ //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1495
+ var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1496
+ pageY = containment ? (!(top - this.offset.click.top < containment[1] || top - this.offset.click.top > containment[3]) ? top : (!(top - this.offset.click.top < containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
1497
+
1498
+ var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1499
+ pageX = containment ? (!(left - this.offset.click.left < containment[0] || left - this.offset.click.left > containment[2]) ? left : (!(left - this.offset.click.left < containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
1500
+ }
1501
+
1502
+ }
1503
+
1504
+ return {
1505
+ top: (
1506
+ pageY // The absolute mouse position
1507
+ - this.offset.click.top // Click offset (relative to the element)
1508
+ - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
1509
+ - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
1510
+ + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
1511
+ ),
1512
+ left: (
1513
+ pageX // The absolute mouse position
1514
+ - this.offset.click.left // Click offset (relative to the element)
1515
+ - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
1516
+ - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
1517
+ + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
1518
+ )
1519
+ };
1520
+
1521
+ },
1522
+
1523
+ _clear: function() {
1524
+ this.helper.removeClass("ui-draggable-dragging");
1525
+ if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
1526
+ //if($.ui.ddmanager) $.ui.ddmanager.current = null;
1527
+ this.helper = null;
1528
+ this.cancelHelperRemoval = false;
1529
+ },
1530
+
1531
+ // From now on bulk stuff - mainly helpers
1532
+
1533
+ _trigger: function(type, event, ui) {
1534
+ ui = ui || this._uiHash();
1535
+ $.ui.plugin.call(this, type, [event, ui]);
1536
+ if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
1537
+ return $.Widget.prototype._trigger.call(this, type, event, ui);
1538
+ },
1539
+
1540
+ plugins: {},
1541
+
1542
+ _uiHash: function(event) {
1543
+ return {
1544
+ helper: this.helper,
1545
+ position: this.position,
1546
+ originalPosition: this.originalPosition,
1547
+ offset: this.positionAbs
1548
+ };
1549
+ }
1550
+
1551
+ });
1552
+
1553
+ $.extend($.ui.draggable, {
1554
+ version: "1.8.17"
1555
+ });
1556
+
1557
+ $.ui.plugin.add("draggable", "connectToSortable", {
1558
+ start: function(event, ui) {
1559
+
1560
+ var inst = $(this).data("draggable"), o = inst.options,
1561
+ uiSortable = $.extend({}, ui, { item: inst.element });
1562
+ inst.sortables = [];
1563
+ $(o.connectToSortable).each(function() {
1564
+ var sortable = $.data(this, 'sortable');
1565
+ if (sortable && !sortable.options.disabled) {
1566
+ inst.sortables.push({
1567
+ instance: sortable,
1568
+ shouldRevert: sortable.options.revert
1569
+ });
1570
+ sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
1571
+ sortable._trigger("activate", event, uiSortable);
1572
+ }
1573
+ });
1574
+
1575
+ },
1576
+ stop: function(event, ui) {
1577
+
1578
+ //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1579
+ var inst = $(this).data("draggable"),
1580
+ uiSortable = $.extend({}, ui, { item: inst.element });
1581
+
1582
+ $.each(inst.sortables, function() {
1583
+ if(this.instance.isOver) {
1584
+
1585
+ this.instance.isOver = 0;
1586
+
1587
+ inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
1588
+ this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1589
+
1590
+ //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
1591
+ if(this.shouldRevert) this.instance.options.revert = true;
1592
+
1593
+ //Trigger the stop of the sortable
1594
+ this.instance._mouseStop(event);
1595
+
1596
+ this.instance.options.helper = this.instance.options._helper;
1597
+
1598
+ //If the helper has been the original item, restore properties in the sortable
1599
+ if(inst.options.helper == 'original')
1600
+ this.instance.currentItem.css({ top: 'auto', left: 'auto' });
1601
+
1602
+ } else {
1603
+ this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1604
+ this.instance._trigger("deactivate", event, uiSortable);
1605
+ }
1606
+
1607
+ });
1608
+
1609
+ },
1610
+ drag: function(event, ui) {
1611
+
1612
+ var inst = $(this).data("draggable"), self = this;
1613
+
1614
+ var checkPos = function(o) {
1615
+ var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
1616
+ var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
1617
+ var itemHeight = o.height, itemWidth = o.width;
1618
+ var itemTop = o.top, itemLeft = o.left;
1619
+
1620
+ return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
1621
+ };
1622
+
1623
+ $.each(inst.sortables, function(i) {
1624
+
1625
+ //Copy over some variables to allow calling the sortable's native _intersectsWith
1626
+ this.instance.positionAbs = inst.positionAbs;
1627
+ this.instance.helperProportions = inst.helperProportions;
1628
+ this.instance.offset.click = inst.offset.click;
1629
+
1630
+ if(this.instance._intersectsWith(this.instance.containerCache)) {
1631
+
1632
+ //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1633
+ if(!this.instance.isOver) {
1634
+
1635
+ this.instance.isOver = 1;
1636
+ //Now we fake the start of dragging for the sortable instance,
1637
+ //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1638
+ //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
1639
+ this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
1640
+ this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1641
+ this.instance.options.helper = function() { return ui.helper[0]; };
1642
+
1643
+ event.target = this.instance.currentItem[0];
1644
+ this.instance._mouseCapture(event, true);
1645
+ this.instance._mouseStart(event, true, true);
1646
+
1647
+ //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1648
+ this.instance.offset.click.top = inst.offset.click.top;
1649
+ this.instance.offset.click.left = inst.offset.click.left;
1650
+ this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
1651
+ this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
1652
+
1653
+ inst._trigger("toSortable", event);
1654
+ inst.dropped = this.instance.element; //draggable revert needs that
1655
+ //hack so receive/update callbacks work (mostly)
1656
+ inst.currentItem = inst.element;
1657
+ this.instance.fromOutside = inst;
1658
+
1659
+ }
1660
+
1661
+ //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
1662
+ if(this.instance.currentItem) this.instance._mouseDrag(event);
1663
+
1664
+ } else {
1665
+
1666
+ //If it doesn't intersect with the sortable, and it intersected before,
1667
+ //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1668
+ if(this.instance.isOver) {
1669
+
1670
+ this.instance.isOver = 0;
1671
+ this.instance.cancelHelperRemoval = true;
1672
+
1673
+ //Prevent reverting on this forced stop
1674
+ this.instance.options.revert = false;
1675
+
1676
+ // The out event needs to be triggered independently
1677
+ this.instance._trigger('out', event, this.instance._uiHash(this.instance));
1678
+
1679
+ this.instance._mouseStop(event, true);
1680
+ this.instance.options.helper = this.instance.options._helper;
1681
+
1682
+ //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1683
+ this.instance.currentItem.remove();
1684
+ if(this.instance.placeholder) this.instance.placeholder.remove();
1685
+
1686
+ inst._trigger("fromSortable", event);
1687
+ inst.dropped = false; //draggable revert needs that
1688
+ }
1689
+
1690
+ };
1691
+
1692
+ });
1693
+
1694
+ }
1695
+ });
1696
+
1697
+ $.ui.plugin.add("draggable", "cursor", {
1698
+ start: function(event, ui) {
1699
+ var t = $('body'), o = $(this).data('draggable').options;
1700
+ if (t.css("cursor")) o._cursor = t.css("cursor");
1701
+ t.css("cursor", o.cursor);
1702
+ },
1703
+ stop: function(event, ui) {
1704
+ var o = $(this).data('draggable').options;
1705
+ if (o._cursor) $('body').css("cursor", o._cursor);
1706
+ }
1707
+ });
1708
+
1709
+ $.ui.plugin.add("draggable", "opacity", {
1710
+ start: function(event, ui) {
1711
+ var t = $(ui.helper), o = $(this).data('draggable').options;
1712
+ if(t.css("opacity")) o._opacity = t.css("opacity");
1713
+ t.css('opacity', o.opacity);
1714
+ },
1715
+ stop: function(event, ui) {
1716
+ var o = $(this).data('draggable').options;
1717
+ if(o._opacity) $(ui.helper).css('opacity', o._opacity);
1718
+ }
1719
+ });
1720
+
1721
+ $.ui.plugin.add("draggable", "scroll", {
1722
+ start: function(event, ui) {
1723
+ var i = $(this).data("draggable");
1724
+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
1725
+ },
1726
+ drag: function(event, ui) {
1727
+
1728
+ var i = $(this).data("draggable"), o = i.options, scrolled = false;
1729
+
1730
+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
1731
+
1732
+ if(!o.axis || o.axis != 'x') {
1733
+ if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
1734
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1735
+ else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
1736
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1737
+ }
1738
+
1739
+ if(!o.axis || o.axis != 'y') {
1740
+ if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
1741
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1742
+ else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
1743
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1744
+ }
1745
+
1746
+ } else {
1747
+
1748
+ if(!o.axis || o.axis != 'x') {
1749
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
1750
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1751
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
1752
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1753
+ }
1754
+
1755
+ if(!o.axis || o.axis != 'y') {
1756
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
1757
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
1758
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
1759
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
1760
+ }
1761
+
1762
+ }
1763
+
1764
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
1765
+ $.ui.ddmanager.prepareOffsets(i, event);
1766
+
1767
+ }
1768
+ });
1769
+
1770
+ $.ui.plugin.add("draggable", "snap", {
1771
+ start: function(event, ui) {
1772
+
1773
+ var i = $(this).data("draggable"), o = i.options;
1774
+ i.snapElements = [];
1775
+
1776
+ $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
1777
+ var $t = $(this); var $o = $t.offset();
1778
+ if(this != i.element[0]) i.snapElements.push({
1779
+ item: this,
1780
+ width: $t.outerWidth(), height: $t.outerHeight(),
1781
+ top: $o.top, left: $o.left
1782
+ });
1783
+ });
1784
+
1785
+ },
1786
+ drag: function(event, ui) {
1787
+
1788
+ var inst = $(this).data("draggable"), o = inst.options;
1789
+ var d = o.snapTolerance;
1790
+
1791
+ var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1792
+ y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1793
+
1794
+ for (var i = inst.snapElements.length - 1; i >= 0; i--){
1795
+
1796
+ var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
1797
+ t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
1798
+
1799
+ //Yes, I know, this is insane ;)
1800
+ if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
1801
+ if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1802
+ inst.snapElements[i].snapping = false;
1803
+ continue;
1804
+ }
1805
+
1806
+ if(o.snapMode != 'inner') {
1807
+ var ts = Math.abs(t - y2) <= d;
1808
+ var bs = Math.abs(b - y1) <= d;
1809
+ var ls = Math.abs(l - x2) <= d;
1810
+ var rs = Math.abs(r - x1) <= d;
1811
+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1812
+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1813
+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1814
+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1815
+ }
1816
+
1817
+ var first = (ts || bs || ls || rs);
1818
+
1819
+ if(o.snapMode != 'outer') {
1820
+ var ts = Math.abs(t - y1) <= d;
1821
+ var bs = Math.abs(b - y2) <= d;
1822
+ var ls = Math.abs(l - x1) <= d;
1823
+ var rs = Math.abs(r - x2) <= d;
1824
+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1825
+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1826
+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1827
+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1828
+ }
1829
+
1830
+ if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
1831
+ (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1832
+ inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1833
+
1834
+ };
1835
+
1836
+ }
1837
+ });
1838
+
1839
+ $.ui.plugin.add("draggable", "stack", {
1840
+ start: function(event, ui) {
1841
+
1842
+ var o = $(this).data("draggable").options;
1843
+
1844
+ var group = $.makeArray($(o.stack)).sort(function(a,b) {
1845
+ return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
1846
+ });
1847
+ if (!group.length) { return; }
1848
+
1849
+ var min = parseInt(group[0].style.zIndex) || 0;
1850
+ $(group).each(function(i) {
1851
+ this.style.zIndex = min + i;
1852
+ });
1853
+
1854
+ this[0].style.zIndex = min + group.length;
1855
+
1856
+ }
1857
+ });
1858
+
1859
+ $.ui.plugin.add("draggable", "zIndex", {
1860
+ start: function(event, ui) {
1861
+ var t = $(ui.helper), o = $(this).data("draggable").options;
1862
+ if(t.css("zIndex")) o._zIndex = t.css("zIndex");
1863
+ t.css('zIndex', o.zIndex);
1864
+ },
1865
+ stop: function(event, ui) {
1866
+ var o = $(this).data("draggable").options;
1867
+ if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
1868
+ }
1869
+ });
1870
+
1871
+ })(jQuery);
1872
+ /*
1873
+ * jQuery UI Droppable 1.8.17
1874
+ *
1875
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
1876
+ * Dual licensed under the MIT or GPL Version 2 licenses.
1877
+ * http://jquery.org/license
1878
+ *
1879
+ * http://docs.jquery.com/UI/Droppables
1880
+ *
1881
+ * Depends:
1882
+ * jquery.ui.core.js
1883
+ * jquery.ui.widget.js
1884
+ * jquery.ui.mouse.js
1885
+ * jquery.ui.draggable.js
1886
+ */
1887
+ (function( $, undefined ) {
1888
+
1889
+ $.widget("ui.droppable", {
1890
+ widgetEventPrefix: "drop",
1891
+ options: {
1892
+ accept: '*',
1893
+ activeClass: false,
1894
+ addClasses: true,
1895
+ greedy: false,
1896
+ hoverClass: false,
1897
+ scope: 'default',
1898
+ tolerance: 'intersect'
1899
+ },
1900
+ _create: function() {
1901
+
1902
+ var o = this.options, accept = o.accept;
1903
+ this.isover = 0; this.isout = 1;
1904
+
1905
+ this.accept = $.isFunction(accept) ? accept : function(d) {
1906
+ return d.is(accept);
1907
+ };
1908
+
1909
+ //Store the droppable's proportions
1910
+ this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
1911
+
1912
+ // Add the reference and positions to the manager
1913
+ $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
1914
+ $.ui.ddmanager.droppables[o.scope].push(this);
1915
+
1916
+ (o.addClasses && this.element.addClass("ui-droppable"));
1917
+
1918
+ },
1919
+
1920
+ destroy: function() {
1921
+ var drop = $.ui.ddmanager.droppables[this.options.scope];
1922
+ for ( var i = 0; i < drop.length; i++ )
1923
+ if ( drop[i] == this )
1924
+ drop.splice(i, 1);
1925
+
1926
+ this.element
1927
+ .removeClass("ui-droppable ui-droppable-disabled")
1928
+ .removeData("droppable")
1929
+ .unbind(".droppable");
1930
+
1931
+ return this;
1932
+ },
1933
+
1934
+ _setOption: function(key, value) {
1935
+
1936
+ if(key == 'accept') {
1937
+ this.accept = $.isFunction(value) ? value : function(d) {
1938
+ return d.is(value);
1939
+ };
1940
+ }
1941
+ $.Widget.prototype._setOption.apply(this, arguments);
1942
+ },
1943
+
1944
+ _activate: function(event) {
1945
+ var draggable = $.ui.ddmanager.current;
1946
+ if(this.options.activeClass) this.element.addClass(this.options.activeClass);
1947
+ (draggable && this._trigger('activate', event, this.ui(draggable)));
1948
+ },
1949
+
1950
+ _deactivate: function(event) {
1951
+ var draggable = $.ui.ddmanager.current;
1952
+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
1953
+ (draggable && this._trigger('deactivate', event, this.ui(draggable)));
1954
+ },
1955
+
1956
+ _over: function(event) {
1957
+
1958
+ var draggable = $.ui.ddmanager.current;
1959
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
1960
+
1961
+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1962
+ if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
1963
+ this._trigger('over', event, this.ui(draggable));
1964
+ }
1965
+
1966
+ },
1967
+
1968
+ _out: function(event) {
1969
+
1970
+ var draggable = $.ui.ddmanager.current;
1971
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
1972
+
1973
+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1974
+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
1975
+ this._trigger('out', event, this.ui(draggable));
1976
+ }
1977
+
1978
+ },
1979
+
1980
+ _drop: function(event,custom) {
1981
+
1982
+ var draggable = custom || $.ui.ddmanager.current;
1983
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
1984
+
1985
+ var childrenIntersection = false;
1986
+ this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
1987
+ var inst = $.data(this, 'droppable');
1988
+ if(
1989
+ inst.options.greedy
1990
+ && !inst.options.disabled
1991
+ && inst.options.scope == draggable.options.scope
1992
+ && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
1993
+ && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
1994
+ ) { childrenIntersection = true; return false; }
1995
+ });
1996
+ if(childrenIntersection) return false;
1997
+
1998
+ if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1999
+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
2000
+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
2001
+ this._trigger('drop', event, this.ui(draggable));
2002
+ return this.element;
2003
+ }
2004
+
2005
+ return false;
2006
+
2007
+ },
2008
+
2009
+ ui: function(c) {
2010
+ return {
2011
+ draggable: (c.currentItem || c.element),
2012
+ helper: c.helper,
2013
+ position: c.position,
2014
+ offset: c.positionAbs
2015
+ };
2016
+ }
2017
+
2018
+ });
2019
+
2020
+ $.extend($.ui.droppable, {
2021
+ version: "1.8.17"
2022
+ });
2023
+
2024
+ $.ui.intersect = function(draggable, droppable, toleranceMode) {
2025
+
2026
+ if (!droppable.offset) return false;
2027
+
2028
+ var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2029
+ y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
2030
+ var l = droppable.offset.left, r = l + droppable.proportions.width,
2031
+ t = droppable.offset.top, b = t + droppable.proportions.height;
2032
+
2033
+ switch (toleranceMode) {
2034
+ case 'fit':
2035
+ return (l <= x1 && x2 <= r
2036
+ && t <= y1 && y2 <= b);
2037
+ break;
2038
+ case 'intersect':
2039
+ return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
2040
+ && x2 - (draggable.helperProportions.width / 2) < r // Left Half
2041
+ && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
2042
+ && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2043
+ break;
2044
+ case 'pointer':
2045
+ var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
2046
+ draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
2047
+ isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
2048
+ return isOver;
2049
+ break;
2050
+ case 'touch':
2051
+ return (
2052
+ (y1 >= t && y1 <= b) || // Top edge touching
2053
+ (y2 >= t && y2 <= b) || // Bottom edge touching
2054
+ (y1 < t && y2 > b) // Surrounded vertically
2055
+ ) && (
2056
+ (x1 >= l && x1 <= r) || // Left edge touching
2057
+ (x2 >= l && x2 <= r) || // Right edge touching
2058
+ (x1 < l && x2 > r) // Surrounded horizontally
2059
+ );
2060
+ break;
2061
+ default:
2062
+ return false;
2063
+ break;
2064
+ }
2065
+
2066
+ };
2067
+
2068
+ /*
2069
+ This manager tracks offsets of draggables and droppables
2070
+ */
2071
+ $.ui.ddmanager = {
2072
+ current: null,
2073
+ droppables: { 'default': [] },
2074
+ prepareOffsets: function(t, event) {
2075
+
2076
+ var m = $.ui.ddmanager.droppables[t.options.scope] || [];
2077
+ var type = event ? event.type : null; // workaround for #2317
2078
+ var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
2079
+
2080
+ droppablesLoop: for (var i = 0; i < m.length; i++) {
2081
+
2082
+ if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
2083
+ for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
2084
+ m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
2085
+
2086
+ if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
2087
+
2088
+ m[i].offset = m[i].element.offset();
2089
+ m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2090
+
2091
+ }
2092
+
2093
+ },
2094
+ drop: function(draggable, event) {
2095
+
2096
+ var dropped = false;
2097
+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2098
+
2099
+ if(!this.options) return;
2100
+ if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
2101
+ dropped = this._drop.call(this, event) || dropped;
2102
+
2103
+ if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2104
+ this.isout = 1; this.isover = 0;
2105
+ this._deactivate.call(this, event);
2106
+ }
2107
+
2108
+ });
2109
+ return dropped;
2110
+
2111
+ },
2112
+ dragStart: function( draggable, event ) {
2113
+ //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2114
+ draggable.element.parents( ":not(body,html)" ).bind( "scroll.droppable", function() {
2115
+ if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
2116
+ });
2117
+ },
2118
+ drag: function(draggable, event) {
2119
+
2120
+ //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2121
+ if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
2122
+
2123
+ //Run through all droppables and check their positions based on specific tolerance options
2124
+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2125
+
2126
+ if(this.options.disabled || this.greedyChild || !this.visible) return;
2127
+ var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
2128
+
2129
+ var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
2130
+ if(!c) return;
2131
+
2132
+ var parentInstance;
2133
+ if (this.options.greedy) {
2134
+ var parent = this.element.parents(':data(droppable):eq(0)');
2135
+ if (parent.length) {
2136
+ parentInstance = $.data(parent[0], 'droppable');
2137
+ parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
2138
+ }
2139
+ }
2140
+
2141
+ // we just moved into a greedy child
2142
+ if (parentInstance && c == 'isover') {
2143
+ parentInstance['isover'] = 0;
2144
+ parentInstance['isout'] = 1;
2145
+ parentInstance._out.call(parentInstance, event);
2146
+ }
2147
+
2148
+ this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
2149
+ this[c == "isover" ? "_over" : "_out"].call(this, event);
2150
+
2151
+ // we just moved out of a greedy child
2152
+ if (parentInstance && c == 'isout') {
2153
+ parentInstance['isout'] = 0;
2154
+ parentInstance['isover'] = 1;
2155
+ parentInstance._over.call(parentInstance, event);
2156
+ }
2157
+ });
2158
+
2159
+ },
2160
+ dragStop: function( draggable, event ) {
2161
+ draggable.element.parents( ":not(body,html)" ).unbind( "scroll.droppable" );
2162
+ //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2163
+ if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
2164
+ }
2165
+ };
2166
+
2167
+ })(jQuery);
2168
+ /*
2169
+ * jQuery UI Resizable 1.8.17
2170
+ *
2171
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
2172
+ * Dual licensed under the MIT or GPL Version 2 licenses.
2173
+ * http://jquery.org/license
2174
+ *
2175
+ * http://docs.jquery.com/UI/Resizables
2176
+ *
2177
+ * Depends:
2178
+ * jquery.ui.core.js
2179
+ * jquery.ui.mouse.js
2180
+ * jquery.ui.widget.js
2181
+ */
2182
+ (function( $, undefined ) {
2183
+
2184
+ $.widget("ui.resizable", $.ui.mouse, {
2185
+ widgetEventPrefix: "resize",
2186
+ options: {
2187
+ alsoResize: false,
2188
+ animate: false,
2189
+ animateDuration: "slow",
2190
+ animateEasing: "swing",
2191
+ aspectRatio: false,
2192
+ autoHide: false,
2193
+ containment: false,
2194
+ ghost: false,
2195
+ grid: false,
2196
+ handles: "e,s,se",
2197
+ helper: false,
2198
+ maxHeight: null,
2199
+ maxWidth: null,
2200
+ minHeight: 10,
2201
+ minWidth: 10,
2202
+ zIndex: 1000
2203
+ },
2204
+ _create: function() {
2205
+
2206
+ var self = this, o = this.options;
2207
+ this.element.addClass("ui-resizable");
2208
+
2209
+ $.extend(this, {
2210
+ _aspectRatio: !!(o.aspectRatio),
2211
+ aspectRatio: o.aspectRatio,
2212
+ originalElement: this.element,
2213
+ _proportionallyResizeElements: [],
2214
+ _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
2215
+ });
2216
+
2217
+ //Wrap the element if it cannot hold child nodes
2218
+ if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2219
+
2220
+ //Opera fix for relative positioning
2221
+ if (/relative/.test(this.element.css('position')) && $.browser.opera)
2222
+ this.element.css({ position: 'relative', top: 'auto', left: 'auto' });
2223
+
2224
+ //Create a wrapper element and set the wrapper to the new current internal element
2225
+ this.element.wrap(
2226
+ $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
2227
+ position: this.element.css('position'),
2228
+ width: this.element.outerWidth(),
2229
+ height: this.element.outerHeight(),
2230
+ top: this.element.css('top'),
2231
+ left: this.element.css('left')
2232
+ })
2233
+ );
2234
+
2235
+ //Overwrite the original this.element
2236
+ this.element = this.element.parent().data(
2237
+ "resizable", this.element.data('resizable')
2238
+ );
2239
+
2240
+ this.elementIsWrapper = true;
2241
+
2242
+ //Move margins to the wrapper
2243
+ this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2244
+ this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2245
+
2246
+ //Prevent Safari textarea resize
2247
+ this.originalResizeStyle = this.originalElement.css('resize');
2248
+ this.originalElement.css('resize', 'none');
2249
+
2250
+ //Push the actual element to our proportionallyResize internal array
2251
+ this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
2252
+
2253
+ // avoid IE jump (hard set the margin)
2254
+ this.originalElement.css({ margin: this.originalElement.css('margin') });
2255
+
2256
+ // fix handlers offset
2257
+ this._proportionallyResize();
2258
+
2259
+ }
2260
+
2261
+ this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' });
2262
+ if(this.handles.constructor == String) {
2263
+
2264
+ if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
2265
+ var n = this.handles.split(","); this.handles = {};
2266
+
2267
+ for(var i = 0; i < n.length; i++) {
2268
+
2269
+ var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
2270
+ var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
2271
+
2272
+ // increase zIndex of sw, se, ne, nw axis
2273
+ //TODO : this modifies original option
2274
+ if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
2275
+
2276
+ //TODO : What's going on here?
2277
+ if ('se' == handle) {
2278
+ axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
2279
+ };
2280
+
2281
+ //Insert into internal handles object and append to element
2282
+ this.handles[handle] = '.ui-resizable-'+handle;
2283
+ this.element.append(axis);
2284
+ }
2285
+
2286
+ }
2287
+
2288
+ this._renderAxis = function(target) {
2289
+
2290
+ target = target || this.element;
2291
+
2292
+ for(var i in this.handles) {
2293
+
2294
+ if(this.handles[i].constructor == String)
2295
+ this.handles[i] = $(this.handles[i], this.element).show();
2296
+
2297
+ //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2298
+ if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2299
+
2300
+ var axis = $(this.handles[i], this.element), padWrapper = 0;
2301
+
2302
+ //Checking the correct pad and border
2303
+ padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2304
+
2305
+ //The padding type i have to apply...
2306
+ var padPos = [ 'padding',
2307
+ /ne|nw|n/.test(i) ? 'Top' :
2308
+ /se|sw|s/.test(i) ? 'Bottom' :
2309
+ /^e$/.test(i) ? 'Right' : 'Left' ].join("");
2310
+
2311
+ target.css(padPos, padWrapper);
2312
+
2313
+ this._proportionallyResize();
2314
+
2315
+ }
2316
+
2317
+ //TODO: What's that good for? There's not anything to be executed left
2318
+ if(!$(this.handles[i]).length)
2319
+ continue;
2320
+
2321
+ }
2322
+ };
2323
+
2324
+ //TODO: make renderAxis a prototype function
2325
+ this._renderAxis(this.element);
2326
+
2327
+ this._handles = $('.ui-resizable-handle', this.element)
2328
+ .disableSelection();
2329
+
2330
+ //Matching axis name
2331
+ this._handles.mouseover(function() {
2332
+ if (!self.resizing) {
2333
+ if (this.className)
2334
+ var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2335
+ //Axis, default = se
2336
+ self.axis = axis && axis[1] ? axis[1] : 'se';
2337
+ }
2338
+ });
2339
+
2340
+ //If we want to auto hide the elements
2341
+ if (o.autoHide) {
2342
+ this._handles.hide();
2343
+ $(this.element)
2344
+ .addClass("ui-resizable-autohide")
2345
+ .hover(function() {
2346
+ if (o.disabled) return;
2347
+ $(this).removeClass("ui-resizable-autohide");
2348
+ self._handles.show();
2349
+ },
2350
+ function(){
2351
+ if (o.disabled) return;
2352
+ if (!self.resizing) {
2353
+ $(this).addClass("ui-resizable-autohide");
2354
+ self._handles.hide();
2355
+ }
2356
+ });
2357
+ }
2358
+
2359
+ //Initialize the mouse interaction
2360
+ this._mouseInit();
2361
+
2362
+ },
2363
+
2364
+ destroy: function() {
2365
+
2366
+ this._mouseDestroy();
2367
+
2368
+ var _destroy = function(exp) {
2369
+ $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2370
+ .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
2371
+ };
2372
+
2373
+ //TODO: Unwrap at same DOM position
2374
+ if (this.elementIsWrapper) {
2375
+ _destroy(this.element);
2376
+ var wrapper = this.element;
2377
+ wrapper.after(
2378
+ this.originalElement.css({
2379
+ position: wrapper.css('position'),
2380
+ width: wrapper.outerWidth(),
2381
+ height: wrapper.outerHeight(),
2382
+ top: wrapper.css('top'),
2383
+ left: wrapper.css('left')
2384
+ })
2385
+ ).remove();
2386
+ }
2387
+
2388
+ this.originalElement.css('resize', this.originalResizeStyle);
2389
+ _destroy(this.originalElement);
2390
+
2391
+ return this;
2392
+ },
2393
+
2394
+ _mouseCapture: function(event) {
2395
+ var handle = false;
2396
+ for (var i in this.handles) {
2397
+ if ($(this.handles[i])[0] == event.target) {
2398
+ handle = true;
2399
+ }
2400
+ }
2401
+
2402
+ return !this.options.disabled && handle;
2403
+ },
2404
+
2405
+ _mouseStart: function(event) {
2406
+
2407
+ var o = this.options, iniPos = this.element.position(), el = this.element;
2408
+
2409
+ this.resizing = true;
2410
+ this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
2411
+
2412
+ // bugfix for http://dev.jquery.com/ticket/1749
2413
+ if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
2414
+ el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
2415
+ }
2416
+
2417
+ //Opera fixing relative position
2418
+ if ($.browser.opera && (/relative/).test(el.css('position')))
2419
+ el.css({ position: 'relative', top: 'auto', left: 'auto' });
2420
+
2421
+ this._renderProxy();
2422
+
2423
+ var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
2424
+
2425
+ if (o.containment) {
2426
+ curleft += $(o.containment).scrollLeft() || 0;
2427
+ curtop += $(o.containment).scrollTop() || 0;
2428
+ }
2429
+
2430
+ //Store needed variables
2431
+ this.offset = this.helper.offset();
2432
+ this.position = { left: curleft, top: curtop };
2433
+ this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2434
+ this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2435
+ this.originalPosition = { left: curleft, top: curtop };
2436
+ this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
2437
+ this.originalMousePosition = { left: event.pageX, top: event.pageY };
2438
+
2439
+ //Aspect Ratio
2440
+ this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
2441
+
2442
+ var cursor = $('.ui-resizable-' + this.axis).css('cursor');
2443
+ $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
2444
+
2445
+ el.addClass("ui-resizable-resizing");
2446
+ this._propagate("start", event);
2447
+ return true;
2448
+ },
2449
+
2450
+ _mouseDrag: function(event) {
2451
+
2452
+ //Increase performance, avoid regex
2453
+ var el = this.helper, o = this.options, props = {},
2454
+ self = this, smp = this.originalMousePosition, a = this.axis;
2455
+
2456
+ var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
2457
+ var trigger = this._change[a];
2458
+ if (!trigger) return false;
2459
+
2460
+ // Calculate the attrs that will be change
2461
+ var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
2462
+
2463
+ // Put this in the mouseDrag handler since the user can start pressing shift while resizing
2464
+ this._updateVirtualBoundaries(event.shiftKey);
2465
+ if (this._aspectRatio || event.shiftKey)
2466
+ data = this._updateRatio(data, event);
2467
+
2468
+ data = this._respectSize(data, event);
2469
+
2470
+ // plugins callbacks need to be called first
2471
+ this._propagate("resize", event);
2472
+
2473
+ el.css({
2474
+ top: this.position.top + "px", left: this.position.left + "px",
2475
+ width: this.size.width + "px", height: this.size.height + "px"
2476
+ });
2477
+
2478
+ if (!this._helper && this._proportionallyResizeElements.length)
2479
+ this._proportionallyResize();
2480
+
2481
+ this._updateCache(data);
2482
+
2483
+ // calling the user callback at the end
2484
+ this._trigger('resize', event, this.ui());
2485
+
2486
+ return false;
2487
+ },
2488
+
2489
+ _mouseStop: function(event) {
2490
+
2491
+ this.resizing = false;
2492
+ var o = this.options, self = this;
2493
+
2494
+ if(this._helper) {
2495
+ var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2496
+ soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
2497
+ soffsetw = ista ? 0 : self.sizeDiff.width;
2498
+
2499
+ var s = { width: (self.helper.width() - soffsetw), height: (self.helper.height() - soffseth) },
2500
+ left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
2501
+ top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
2502
+
2503
+ if (!o.animate)
2504
+ this.element.css($.extend(s, { top: top, left: left }));
2505
+
2506
+ self.helper.height(self.size.height);
2507
+ self.helper.width(self.size.width);
2508
+
2509
+ if (this._helper && !o.animate) this._proportionallyResize();
2510
+ }
2511
+
2512
+ $('body').css('cursor', 'auto');
2513
+
2514
+ this.element.removeClass("ui-resizable-resizing");
2515
+
2516
+ this._propagate("stop", event);
2517
+
2518
+ if (this._helper) this.helper.remove();
2519
+ return false;
2520
+
2521
+ },
2522
+
2523
+ _updateVirtualBoundaries: function(forceAspectRatio) {
2524
+ var o = this.options, pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b;
2525
+
2526
+ b = {
2527
+ minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
2528
+ maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
2529
+ minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
2530
+ maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
2531
+ };
2532
+
2533
+ if(this._aspectRatio || forceAspectRatio) {
2534
+ // We want to create an enclosing box whose aspect ration is the requested one
2535
+ // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
2536
+ pMinWidth = b.minHeight * this.aspectRatio;
2537
+ pMinHeight = b.minWidth / this.aspectRatio;
2538
+ pMaxWidth = b.maxHeight * this.aspectRatio;
2539
+ pMaxHeight = b.maxWidth / this.aspectRatio;
2540
+
2541
+ if(pMinWidth > b.minWidth) b.minWidth = pMinWidth;
2542
+ if(pMinHeight > b.minHeight) b.minHeight = pMinHeight;
2543
+ if(pMaxWidth < b.maxWidth) b.maxWidth = pMaxWidth;
2544
+ if(pMaxHeight < b.maxHeight) b.maxHeight = pMaxHeight;
2545
+ }
2546
+ this._vBoundaries = b;
2547
+ },
2548
+
2549
+ _updateCache: function(data) {
2550
+ var o = this.options;
2551
+ this.offset = this.helper.offset();
2552
+ if (isNumber(data.left)) this.position.left = data.left;
2553
+ if (isNumber(data.top)) this.position.top = data.top;
2554
+ if (isNumber(data.height)) this.size.height = data.height;
2555
+ if (isNumber(data.width)) this.size.width = data.width;
2556
+ },
2557
+
2558
+ _updateRatio: function(data, event) {
2559
+
2560
+ var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
2561
+
2562
+ if (isNumber(data.height)) data.width = (data.height * this.aspectRatio);
2563
+ else if (isNumber(data.width)) data.height = (data.width / this.aspectRatio);
2564
+
2565
+ if (a == 'sw') {
2566
+ data.left = cpos.left + (csize.width - data.width);
2567
+ data.top = null;
2568
+ }
2569
+ if (a == 'nw') {
2570
+ data.top = cpos.top + (csize.height - data.height);
2571
+ data.left = cpos.left + (csize.width - data.width);
2572
+ }
2573
+
2574
+ return data;
2575
+ },
2576
+
2577
+ _respectSize: function(data, event) {
2578
+
2579
+ var el = this.helper, o = this._vBoundaries, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
2580
+ ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
2581
+ isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
2582
+
2583
+ if (isminw) data.width = o.minWidth;
2584
+ if (isminh) data.height = o.minHeight;
2585
+ if (ismaxw) data.width = o.maxWidth;
2586
+ if (ismaxh) data.height = o.maxHeight;
2587
+
2588
+ var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
2589
+ var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
2590
+
2591
+ if (isminw && cw) data.left = dw - o.minWidth;
2592
+ if (ismaxw && cw) data.left = dw - o.maxWidth;
2593
+ if (isminh && ch) data.top = dh - o.minHeight;
2594
+ if (ismaxh && ch) data.top = dh - o.maxHeight;
2595
+
2596
+ // fixing jump error on top/left - bug #2330
2597
+ var isNotwh = !data.width && !data.height;
2598
+ if (isNotwh && !data.left && data.top) data.top = null;
2599
+ else if (isNotwh && !data.top && data.left) data.left = null;
2600
+
2601
+ return data;
2602
+ },
2603
+
2604
+ _proportionallyResize: function() {
2605
+
2606
+ var o = this.options;
2607
+ if (!this._proportionallyResizeElements.length) return;
2608
+ var element = this.helper || this.element;
2609
+
2610
+ for (var i=0; i < this._proportionallyResizeElements.length; i++) {
2611
+
2612
+ var prel = this._proportionallyResizeElements[i];
2613
+
2614
+ if (!this.borderDif) {
2615
+ var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
2616
+ p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
2617
+
2618
+ this.borderDif = $.map(b, function(v, i) {
2619
+ var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
2620
+ return border + padding;
2621
+ });
2622
+ }
2623
+
2624
+ if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length)))
2625
+ continue;
2626
+
2627
+ prel.css({
2628
+ height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
2629
+ width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
2630
+ });
2631
+
2632
+ };
2633
+
2634
+ },
2635
+
2636
+ _renderProxy: function() {
2637
+
2638
+ var el = this.element, o = this.options;
2639
+ this.elementOffset = el.offset();
2640
+
2641
+ if(this._helper) {
2642
+
2643
+ this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
2644
+
2645
+ // fix ie6 offset TODO: This seems broken
2646
+ var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
2647
+ pxyoffset = ( ie6 ? 2 : -1 );
2648
+
2649
+ this.helper.addClass(this._helper).css({
2650
+ width: this.element.outerWidth() + pxyoffset,
2651
+ height: this.element.outerHeight() + pxyoffset,
2652
+ position: 'absolute',
2653
+ left: this.elementOffset.left - ie6offset +'px',
2654
+ top: this.elementOffset.top - ie6offset +'px',
2655
+ zIndex: ++o.zIndex //TODO: Don't modify option
2656
+ });
2657
+
2658
+ this.helper
2659
+ .appendTo("body")
2660
+ .disableSelection();
2661
+
2662
+ } else {
2663
+ this.helper = this.element;
2664
+ }
2665
+
2666
+ },
2667
+
2668
+ _change: {
2669
+ e: function(event, dx, dy) {
2670
+ return { width: this.originalSize.width + dx };
2671
+ },
2672
+ w: function(event, dx, dy) {
2673
+ var o = this.options, cs = this.originalSize, sp = this.originalPosition;
2674
+ return { left: sp.left + dx, width: cs.width - dx };
2675
+ },
2676
+ n: function(event, dx, dy) {
2677
+ var o = this.options, cs = this.originalSize, sp = this.originalPosition;
2678
+ return { top: sp.top + dy, height: cs.height - dy };
2679
+ },
2680
+ s: function(event, dx, dy) {
2681
+ return { height: this.originalSize.height + dy };
2682
+ },
2683
+ se: function(event, dx, dy) {
2684
+ return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2685
+ },
2686
+ sw: function(event, dx, dy) {
2687
+ return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2688
+ },
2689
+ ne: function(event, dx, dy) {
2690
+ return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2691
+ },
2692
+ nw: function(event, dx, dy) {
2693
+ return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2694
+ }
2695
+ },
2696
+
2697
+ _propagate: function(n, event) {
2698
+ $.ui.plugin.call(this, n, [event, this.ui()]);
2699
+ (n != "resize" && this._trigger(n, event, this.ui()));
2700
+ },
2701
+
2702
+ plugins: {},
2703
+
2704
+ ui: function() {
2705
+ return {
2706
+ originalElement: this.originalElement,
2707
+ element: this.element,
2708
+ helper: this.helper,
2709
+ position: this.position,
2710
+ size: this.size,
2711
+ originalSize: this.originalSize,
2712
+ originalPosition: this.originalPosition
2713
+ };
2714
+ }
2715
+
2716
+ });
2717
+
2718
+ $.extend($.ui.resizable, {
2719
+ version: "1.8.17"
2720
+ });
2721
+
2722
+ /*
2723
+ * Resizable Extensions
2724
+ */
2725
+
2726
+ $.ui.plugin.add("resizable", "alsoResize", {
2727
+
2728
+ start: function (event, ui) {
2729
+ var self = $(this).data("resizable"), o = self.options;
2730
+
2731
+ var _store = function (exp) {
2732
+ $(exp).each(function() {
2733
+ var el = $(this);
2734
+ el.data("resizable-alsoresize", {
2735
+ width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
2736
+ left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10),
2737
+ position: el.css('position') // to reset Opera on stop()
2738
+ });
2739
+ });
2740
+ };
2741
+
2742
+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
2743
+ if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
2744
+ else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
2745
+ }else{
2746
+ _store(o.alsoResize);
2747
+ }
2748
+ },
2749
+
2750
+ resize: function (event, ui) {
2751
+ var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
2752
+
2753
+ var delta = {
2754
+ height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
2755
+ top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
2756
+ },
2757
+
2758
+ _alsoResize = function (exp, c) {
2759
+ $(exp).each(function() {
2760
+ var el = $(this), start = $(this).data("resizable-alsoresize"), style = {},
2761
+ css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left'];
2762
+
2763
+ $.each(css, function (i, prop) {
2764
+ var sum = (start[prop]||0) + (delta[prop]||0);
2765
+ if (sum && sum >= 0)
2766
+ style[prop] = sum || null;
2767
+ });
2768
+
2769
+ // Opera fixing relative position
2770
+ if ($.browser.opera && /relative/.test(el.css('position'))) {
2771
+ self._revertToRelativePosition = true;
2772
+ el.css({ position: 'absolute', top: 'auto', left: 'auto' });
2773
+ }
2774
+
2775
+ el.css(style);
2776
+ });
2777
+ };
2778
+
2779
+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
2780
+ $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
2781
+ }else{
2782
+ _alsoResize(o.alsoResize);
2783
+ }
2784
+ },
2785
+
2786
+ stop: function (event, ui) {
2787
+ var self = $(this).data("resizable"), o = self.options;
2788
+
2789
+ var _reset = function (exp) {
2790
+ $(exp).each(function() {
2791
+ var el = $(this);
2792
+ // reset position for Opera - no need to verify it was changed
2793
+ el.css({ position: el.data("resizable-alsoresize").position });
2794
+ });
2795
+ };
2796
+
2797
+ if (self._revertToRelativePosition) {
2798
+ self._revertToRelativePosition = false;
2799
+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
2800
+ $.each(o.alsoResize, function (exp) { _reset(exp); });
2801
+ }else{
2802
+ _reset(o.alsoResize);
2803
+ }
2804
+ }
2805
+
2806
+ $(this).removeData("resizable-alsoresize");
2807
+ }
2808
+ });
2809
+
2810
+ $.ui.plugin.add("resizable", "animate", {
2811
+
2812
+ stop: function(event, ui) {
2813
+ var self = $(this).data("resizable"), o = self.options;
2814
+
2815
+ var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2816
+ soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
2817
+ soffsetw = ista ? 0 : self.sizeDiff.width;
2818
+
2819
+ var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
2820
+ left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
2821
+ top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
2822
+
2823
+ self.element.animate(
2824
+ $.extend(style, top && left ? { top: top, left: left } : {}), {
2825
+ duration: o.animateDuration,
2826
+ easing: o.animateEasing,
2827
+ step: function() {
2828
+
2829
+ var data = {
2830
+ width: parseInt(self.element.css('width'), 10),
2831
+ height: parseInt(self.element.css('height'), 10),
2832
+ top: parseInt(self.element.css('top'), 10),
2833
+ left: parseInt(self.element.css('left'), 10)
2834
+ };
2835
+
2836
+ if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
2837
+
2838
+ // propagating resize, and updating values for each animation step
2839
+ self._updateCache(data);
2840
+ self._propagate("resize", event);
2841
+
2842
+ }
2843
+ }
2844
+ );
2845
+ }
2846
+
2847
+ });
2848
+
2849
+ $.ui.plugin.add("resizable", "containment", {
2850
+
2851
+ start: function(event, ui) {
2852
+ var self = $(this).data("resizable"), o = self.options, el = self.element;
2853
+ var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
2854
+ if (!ce) return;
2855
+
2856
+ self.containerElement = $(ce);
2857
+
2858
+ if (/document/.test(oc) || oc == document) {
2859
+ self.containerOffset = { left: 0, top: 0 };
2860
+ self.containerPosition = { left: 0, top: 0 };
2861
+
2862
+ self.parentData = {
2863
+ element: $(document), left: 0, top: 0,
2864
+ width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
2865
+ };
2866
+ }
2867
+
2868
+ // i'm a node, so compute top, left, right, bottom
2869
+ else {
2870
+ var element = $(ce), p = [];
2871
+ $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
2872
+
2873
+ self.containerOffset = element.offset();
2874
+ self.containerPosition = element.position();
2875
+ self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
2876
+
2877
+ var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width,
2878
+ width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
2879
+
2880
+ self.parentData = {
2881
+ element: ce, left: co.left, top: co.top, width: width, height: height
2882
+ };
2883
+ }
2884
+ },
2885
+
2886
+ resize: function(event, ui) {
2887
+ var self = $(this).data("resizable"), o = self.options,
2888
+ ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
2889
+ pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement;
2890
+
2891
+ if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
2892
+
2893
+ if (cp.left < (self._helper ? co.left : 0)) {
2894
+ self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left));
2895
+ if (pRatio) self.size.height = self.size.width / o.aspectRatio;
2896
+ self.position.left = o.helper ? co.left : 0;
2897
+ }
2898
+
2899
+ if (cp.top < (self._helper ? co.top : 0)) {
2900
+ self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top);
2901
+ if (pRatio) self.size.width = self.size.height * o.aspectRatio;
2902
+ self.position.top = self._helper ? co.top : 0;
2903
+ }
2904
+
2905
+ self.offset.left = self.parentData.left+self.position.left;
2906
+ self.offset.top = self.parentData.top+self.position.top;
2907
+
2908
+ var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ),
2909
+ hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height );
2910
+
2911
+ var isParent = self.containerElement.get(0) == self.element.parent().get(0),
2912
+ isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position'));
2913
+
2914
+ if(isParent && isOffsetRelative) woset -= self.parentData.left;
2915
+
2916
+ if (woset + self.size.width >= self.parentData.width) {
2917
+ self.size.width = self.parentData.width - woset;
2918
+ if (pRatio) self.size.height = self.size.width / self.aspectRatio;
2919
+ }
2920
+
2921
+ if (hoset + self.size.height >= self.parentData.height) {
2922
+ self.size.height = self.parentData.height - hoset;
2923
+ if (pRatio) self.size.width = self.size.height * self.aspectRatio;
2924
+ }
2925
+ },
2926
+
2927
+ stop: function(event, ui){
2928
+ var self = $(this).data("resizable"), o = self.options, cp = self.position,
2929
+ co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
2930
+
2931
+ var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height;
2932
+
2933
+ if (self._helper && !o.animate && (/relative/).test(ce.css('position')))
2934
+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
2935
+
2936
+ if (self._helper && !o.animate && (/static/).test(ce.css('position')))
2937
+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
2938
+
2939
+ }
2940
+ });
2941
+
2942
+ $.ui.plugin.add("resizable", "ghost", {
2943
+
2944
+ start: function(event, ui) {
2945
+
2946
+ var self = $(this).data("resizable"), o = self.options, cs = self.size;
2947
+
2948
+ self.ghost = self.originalElement.clone();
2949
+ self.ghost
2950
+ .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
2951
+ .addClass('ui-resizable-ghost')
2952
+ .addClass(typeof o.ghost == 'string' ? o.ghost : '');
2953
+
2954
+ self.ghost.appendTo(self.helper);
2955
+
2956
+ },
2957
+
2958
+ resize: function(event, ui){
2959
+ var self = $(this).data("resizable"), o = self.options;
2960
+ if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
2961
+ },
2962
+
2963
+ stop: function(event, ui){
2964
+ var self = $(this).data("resizable"), o = self.options;
2965
+ if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
2966
+ }
2967
+
2968
+ });
2969
+
2970
+ $.ui.plugin.add("resizable", "grid", {
2971
+
2972
+ resize: function(event, ui) {
2973
+ var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey;
2974
+ o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
2975
+ var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
2976
+
2977
+ if (/^(se|s|e)$/.test(a)) {
2978
+ self.size.width = os.width + ox;
2979
+ self.size.height = os.height + oy;
2980
+ }
2981
+ else if (/^(ne)$/.test(a)) {
2982
+ self.size.width = os.width + ox;
2983
+ self.size.height = os.height + oy;
2984
+ self.position.top = op.top - oy;
2985
+ }
2986
+ else if (/^(sw)$/.test(a)) {
2987
+ self.size.width = os.width + ox;
2988
+ self.size.height = os.height + oy;
2989
+ self.position.left = op.left - ox;
2990
+ }
2991
+ else {
2992
+ self.size.width = os.width + ox;
2993
+ self.size.height = os.height + oy;
2994
+ self.position.top = op.top - oy;
2995
+ self.position.left = op.left - ox;
2996
+ }
2997
+ }
2998
+
2999
+ });
3000
+
3001
+ var num = function(v) {
3002
+ return parseInt(v, 10) || 0;
3003
+ };
3004
+
3005
+ var isNumber = function(value) {
3006
+ return !isNaN(parseInt(value, 10));
3007
+ };
3008
+
3009
+ })(jQuery);
3010
+ /*
3011
+ * jQuery UI Selectable 1.8.17
3012
+ *
3013
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
3014
+ * Dual licensed under the MIT or GPL Version 2 licenses.
3015
+ * http://jquery.org/license
3016
+ *
3017
+ * http://docs.jquery.com/UI/Selectables
3018
+ *
3019
+ * Depends:
3020
+ * jquery.ui.core.js
3021
+ * jquery.ui.mouse.js
3022
+ * jquery.ui.widget.js
3023
+ */
3024
+ (function( $, undefined ) {
3025
+
3026
+ $.widget("ui.selectable", $.ui.mouse, {
3027
+ options: {
3028
+ appendTo: 'body',
3029
+ autoRefresh: true,
3030
+ distance: 0,
3031
+ filter: '*',
3032
+ tolerance: 'touch'
3033
+ },
3034
+ _create: function() {
3035
+ var self = this;
3036
+
3037
+ this.element.addClass("ui-selectable");
3038
+
3039
+ this.dragged = false;
3040
+
3041
+ // cache selectee children based on filter
3042
+ var selectees;
3043
+ this.refresh = function() {
3044
+ selectees = $(self.options.filter, self.element[0]);
3045
+ selectees.addClass("ui-selectee");
3046
+ selectees.each(function() {
3047
+ var $this = $(this);
3048
+ var pos = $this.offset();
3049
+ $.data(this, "selectable-item", {
3050
+ element: this,
3051
+ $element: $this,
3052
+ left: pos.left,
3053
+ top: pos.top,
3054
+ right: pos.left + $this.outerWidth(),
3055
+ bottom: pos.top + $this.outerHeight(),
3056
+ startselected: false,
3057
+ selected: $this.hasClass('ui-selected'),
3058
+ selecting: $this.hasClass('ui-selecting'),
3059
+ unselecting: $this.hasClass('ui-unselecting')
3060
+ });
3061
+ });
3062
+ };
3063
+ this.refresh();
3064
+
3065
+ this.selectees = selectees.addClass("ui-selectee");
3066
+
3067
+ this._mouseInit();
3068
+
3069
+ this.helper = $("<div class='ui-selectable-helper'></div>");
3070
+ },
3071
+
3072
+ destroy: function() {
3073
+ this.selectees
3074
+ .removeClass("ui-selectee")
3075
+ .removeData("selectable-item");
3076
+ this.element
3077
+ .removeClass("ui-selectable ui-selectable-disabled")
3078
+ .removeData("selectable")
3079
+ .unbind(".selectable");
3080
+ this._mouseDestroy();
3081
+
3082
+ return this;
3083
+ },
3084
+
3085
+ _mouseStart: function(event) {
3086
+ var self = this;
3087
+
3088
+ this.opos = [event.pageX, event.pageY];
3089
+
3090
+ if (this.options.disabled)
3091
+ return;
3092
+
3093
+ var options = this.options;
3094
+
3095
+ this.selectees = $(options.filter, this.element[0]);
3096
+
3097
+ this._trigger("start", event);
3098
+
3099
+ $(options.appendTo).append(this.helper);
3100
+ // position helper (lasso)
3101
+ this.helper.css({
3102
+ "left": event.clientX,
3103
+ "top": event.clientY,
3104
+ "width": 0,
3105
+ "height": 0
3106
+ });
3107
+
3108
+ if (options.autoRefresh) {
3109
+ this.refresh();
3110
+ }
3111
+
3112
+ this.selectees.filter('.ui-selected').each(function() {
3113
+ var selectee = $.data(this, "selectable-item");
3114
+ selectee.startselected = true;
3115
+ if (!event.metaKey && !event.ctrlKey) {
3116
+ selectee.$element.removeClass('ui-selected');
3117
+ selectee.selected = false;
3118
+ selectee.$element.addClass('ui-unselecting');
3119
+ selectee.unselecting = true;
3120
+ // selectable UNSELECTING callback
3121
+ self._trigger("unselecting", event, {
3122
+ unselecting: selectee.element
3123
+ });
3124
+ }
3125
+ });
3126
+
3127
+ $(event.target).parents().andSelf().each(function() {
3128
+ var selectee = $.data(this, "selectable-item");
3129
+ if (selectee) {
3130
+ var doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass('ui-selected');
3131
+ selectee.$element
3132
+ .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3133
+ .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3134
+ selectee.unselecting = !doSelect;
3135
+ selectee.selecting = doSelect;
3136
+ selectee.selected = doSelect;
3137
+ // selectable (UN)SELECTING callback
3138
+ if (doSelect) {
3139
+ self._trigger("selecting", event, {
3140
+ selecting: selectee.element
3141
+ });
3142
+ } else {
3143
+ self._trigger("unselecting", event, {
3144
+ unselecting: selectee.element
3145
+ });
3146
+ }
3147
+ return false;
3148
+ }
3149
+ });
3150
+
3151
+ },
3152
+
3153
+ _mouseDrag: function(event) {
3154
+ var self = this;
3155
+ this.dragged = true;
3156
+
3157
+ if (this.options.disabled)
3158
+ return;
3159
+
3160
+ var options = this.options;
3161
+
3162
+ var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
3163
+ if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
3164
+ if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
3165
+ this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3166
+
3167
+ this.selectees.each(function() {
3168
+ var selectee = $.data(this, "selectable-item");
3169
+ //prevent helper from being selected if appendTo: selectable
3170
+ if (!selectee || selectee.element == self.element[0])
3171
+ return;
3172
+ var hit = false;
3173
+ if (options.tolerance == 'touch') {
3174
+ hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3175
+ } else if (options.tolerance == 'fit') {
3176
+ hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3177
+ }
3178
+
3179
+ if (hit) {
3180
+ // SELECT
3181
+ if (selectee.selected) {
3182
+ selectee.$element.removeClass('ui-selected');
3183
+ selectee.selected = false;
3184
+ }
3185
+ if (selectee.unselecting) {
3186
+ selectee.$element.removeClass('ui-unselecting');
3187
+ selectee.unselecting = false;
3188
+ }
3189
+ if (!selectee.selecting) {
3190
+ selectee.$element.addClass('ui-selecting');
3191
+ selectee.selecting = true;
3192
+ // selectable SELECTING callback
3193
+ self._trigger("selecting", event, {
3194
+ selecting: selectee.element
3195
+ });
3196
+ }
3197
+ } else {
3198
+ // UNSELECT
3199
+ if (selectee.selecting) {
3200
+ if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3201
+ selectee.$element.removeClass('ui-selecting');
3202
+ selectee.selecting = false;
3203
+ selectee.$element.addClass('ui-selected');
3204
+ selectee.selected = true;
3205
+ } else {
3206
+ selectee.$element.removeClass('ui-selecting');
3207
+ selectee.selecting = false;
3208
+ if (selectee.startselected) {
3209
+ selectee.$element.addClass('ui-unselecting');
3210
+ selectee.unselecting = true;
3211
+ }
3212
+ // selectable UNSELECTING callback
3213
+ self._trigger("unselecting", event, {
3214
+ unselecting: selectee.element
3215
+ });
3216
+ }
3217
+ }
3218
+ if (selectee.selected) {
3219
+ if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3220
+ selectee.$element.removeClass('ui-selected');
3221
+ selectee.selected = false;
3222
+
3223
+ selectee.$element.addClass('ui-unselecting');
3224
+ selectee.unselecting = true;
3225
+ // selectable UNSELECTING callback
3226
+ self._trigger("unselecting", event, {
3227
+ unselecting: selectee.element
3228
+ });
3229
+ }
3230
+ }
3231
+ }
3232
+ });
3233
+
3234
+ return false;
3235
+ },
3236
+
3237
+ _mouseStop: function(event) {
3238
+ var self = this;
3239
+
3240
+ this.dragged = false;
3241
+
3242
+ var options = this.options;
3243
+
3244
+ $('.ui-unselecting', this.element[0]).each(function() {
3245
+ var selectee = $.data(this, "selectable-item");
3246
+ selectee.$element.removeClass('ui-unselecting');
3247
+ selectee.unselecting = false;
3248
+ selectee.startselected = false;
3249
+ self._trigger("unselected", event, {
3250
+ unselected: selectee.element
3251
+ });
3252
+ });
3253
+ $('.ui-selecting', this.element[0]).each(function() {
3254
+ var selectee = $.data(this, "selectable-item");
3255
+ selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
3256
+ selectee.selecting = false;
3257
+ selectee.selected = true;
3258
+ selectee.startselected = true;
3259
+ self._trigger("selected", event, {
3260
+ selected: selectee.element
3261
+ });
3262
+ });
3263
+ this._trigger("stop", event);
3264
+
3265
+ this.helper.remove();
3266
+
3267
+ return false;
3268
+ }
3269
+
3270
+ });
3271
+
3272
+ $.extend($.ui.selectable, {
3273
+ version: "1.8.17"
3274
+ });
3275
+
3276
+ })(jQuery);
3277
+ /*
3278
+ * jQuery UI Sortable 1.8.17
3279
+ *
3280
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
3281
+ * Dual licensed under the MIT or GPL Version 2 licenses.
3282
+ * http://jquery.org/license
3283
+ *
3284
+ * http://docs.jquery.com/UI/Sortables
3285
+ *
3286
+ * Depends:
3287
+ * jquery.ui.core.js
3288
+ * jquery.ui.mouse.js
3289
+ * jquery.ui.widget.js
3290
+ */
3291
+ (function( $, undefined ) {
3292
+
3293
+ $.widget("ui.sortable", $.ui.mouse, {
3294
+ widgetEventPrefix: "sort",
3295
+ options: {
3296
+ appendTo: "parent",
3297
+ axis: false,
3298
+ connectWith: false,
3299
+ containment: false,
3300
+ cursor: 'auto',
3301
+ cursorAt: false,
3302
+ dropOnEmpty: true,
3303
+ forcePlaceholderSize: false,
3304
+ forceHelperSize: false,
3305
+ grid: false,
3306
+ handle: false,
3307
+ helper: "original",
3308
+ items: '> *',
3309
+ opacity: false,
3310
+ placeholder: false,
3311
+ revert: false,
3312
+ scroll: true,
3313
+ scrollSensitivity: 20,
3314
+ scrollSpeed: 20,
3315
+ scope: "default",
3316
+ tolerance: "intersect",
3317
+ zIndex: 1000
3318
+ },
3319
+ _create: function() {
3320
+
3321
+ var o = this.options;
3322
+ this.containerCache = {};
3323
+ this.element.addClass("ui-sortable");
3324
+
3325
+ //Get the items
3326
+ this.refresh();
3327
+
3328
+ //Let's determine if the items are being displayed horizontally
3329
+ this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
3330
+
3331
+ //Let's determine the parent's offset
3332
+ this.offset = this.element.offset();
3333
+
3334
+ //Initialize mouse events for interaction
3335
+ this._mouseInit();
3336
+
3337
+ },
3338
+
3339
+ destroy: function() {
3340
+ this.element
3341
+ .removeClass("ui-sortable ui-sortable-disabled");
3342
+ this._mouseDestroy();
3343
+
3344
+ for ( var i = this.items.length - 1; i >= 0; i-- )
3345
+ this.items[i].item.removeData(this.widgetName + "-item");
3346
+
3347
+ return this;
3348
+ },
3349
+
3350
+ _setOption: function(key, value){
3351
+ if ( key === "disabled" ) {
3352
+ this.options[ key ] = value;
3353
+
3354
+ this.widget()
3355
+ [ value ? "addClass" : "removeClass"]( "ui-sortable-disabled" );
3356
+ } else {
3357
+ // Don't call widget base _setOption for disable as it adds ui-state-disabled class
3358
+ $.Widget.prototype._setOption.apply(this, arguments);
3359
+ }
3360
+ },
3361
+
3362
+ _mouseCapture: function(event, overrideHandle) {
3363
+ var that = this;
3364
+
3365
+ if (this.reverting) {
3366
+ return false;
3367
+ }
3368
+
3369
+ if(this.options.disabled || this.options.type == 'static') return false;
3370
+
3371
+ //We have to refresh the items data once first
3372
+ this._refreshItems(event);
3373
+
3374
+ //Find out if the clicked node (or one of its parents) is a actual item in this.items
3375
+ var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
3376
+ if($.data(this, that.widgetName + '-item') == self) {
3377
+ currentItem = $(this);
3378
+ return false;
3379
+ }
3380
+ });
3381
+ if($.data(event.target, that.widgetName + '-item') == self) currentItem = $(event.target);
3382
+
3383
+ if(!currentItem) return false;
3384
+ if(this.options.handle && !overrideHandle) {
3385
+ var validHandle = false;
3386
+
3387
+ $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
3388
+ if(!validHandle) return false;
3389
+ }
3390
+
3391
+ this.currentItem = currentItem;
3392
+ this._removeCurrentsFromItems();
3393
+ return true;
3394
+
3395
+ },
3396
+
3397
+ _mouseStart: function(event, overrideHandle, noActivation) {
3398
+
3399
+ var o = this.options, self = this;
3400
+ this.currentContainer = this;
3401
+
3402
+ //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
3403
+ this.refreshPositions();
3404
+
3405
+ //Create and append the visible helper
3406
+ this.helper = this._createHelper(event);
3407
+
3408
+ //Cache the helper size
3409
+ this._cacheHelperProportions();
3410
+
3411
+ /*
3412
+ * - Position generation -
3413
+ * This block generates everything position related - it's the core of draggables.
3414
+ */
3415
+
3416
+ //Cache the margins of the original element
3417
+ this._cacheMargins();
3418
+
3419
+ //Get the next scrolling parent
3420
+ this.scrollParent = this.helper.scrollParent();
3421
+
3422
+ //The element's absolute position on the page minus margins
3423
+ this.offset = this.currentItem.offset();
3424
+ this.offset = {
3425
+ top: this.offset.top - this.margins.top,
3426
+ left: this.offset.left - this.margins.left
3427
+ };
3428
+
3429
+ // Only after we got the offset, we can change the helper's position to absolute
3430
+ // TODO: Still need to figure out a way to make relative sorting possible
3431
+ this.helper.css("position", "absolute");
3432
+ this.cssPosition = this.helper.css("position");
3433
+
3434
+ $.extend(this.offset, {
3435
+ click: { //Where the click happened, relative to the element
3436
+ left: event.pageX - this.offset.left,
3437
+ top: event.pageY - this.offset.top
3438
+ },
3439
+ parent: this._getParentOffset(),
3440
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
3441
+ });
3442
+
3443
+ //Generate the original position
3444
+ this.originalPosition = this._generatePosition(event);
3445
+ this.originalPageX = event.pageX;
3446
+ this.originalPageY = event.pageY;
3447
+
3448
+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
3449
+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
3450
+
3451
+ //Cache the former DOM position
3452
+ this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
3453
+
3454
+ //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
3455
+ if(this.helper[0] != this.currentItem[0]) {
3456
+ this.currentItem.hide();
3457
+ }
3458
+
3459
+ //Create the placeholder
3460
+ this._createPlaceholder();
3461
+
3462
+ //Set a containment if given in the options
3463
+ if(o.containment)
3464
+ this._setContainment();
3465
+
3466
+ if(o.cursor) { // cursor option
3467
+ if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
3468
+ $('body').css("cursor", o.cursor);
3469
+ }
3470
+
3471
+ if(o.opacity) { // opacity option
3472
+ if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
3473
+ this.helper.css("opacity", o.opacity);
3474
+ }
3475
+
3476
+ if(o.zIndex) { // zIndex option
3477
+ if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
3478
+ this.helper.css("zIndex", o.zIndex);
3479
+ }
3480
+
3481
+ //Prepare scrolling
3482
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
3483
+ this.overflowOffset = this.scrollParent.offset();
3484
+
3485
+ //Call callbacks
3486
+ this._trigger("start", event, this._uiHash());
3487
+
3488
+ //Recache the helper size
3489
+ if(!this._preserveHelperProportions)
3490
+ this._cacheHelperProportions();
3491
+
3492
+
3493
+ //Post 'activate' events to possible containers
3494
+ if(!noActivation) {
3495
+ for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); }
3496
+ }
3497
+
3498
+ //Prepare possible droppables
3499
+ if($.ui.ddmanager)
3500
+ $.ui.ddmanager.current = this;
3501
+
3502
+ if ($.ui.ddmanager && !o.dropBehaviour)
3503
+ $.ui.ddmanager.prepareOffsets(this, event);
3504
+
3505
+ this.dragging = true;
3506
+
3507
+ this.helper.addClass("ui-sortable-helper");
3508
+ this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
3509
+ return true;
3510
+
3511
+ },
3512
+
3513
+ _mouseDrag: function(event) {
3514
+
3515
+ //Compute the helpers position
3516
+ this.position = this._generatePosition(event);
3517
+ this.positionAbs = this._convertPositionTo("absolute");
3518
+
3519
+ if (!this.lastPositionAbs) {
3520
+ this.lastPositionAbs = this.positionAbs;
3521
+ }
3522
+
3523
+ //Do scrolling
3524
+ if(this.options.scroll) {
3525
+ var o = this.options, scrolled = false;
3526
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
3527
+
3528
+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
3529
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
3530
+ else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
3531
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
3532
+
3533
+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
3534
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
3535
+ else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
3536
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
3537
+
3538
+ } else {
3539
+
3540
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
3541
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
3542
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
3543
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
3544
+
3545
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
3546
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
3547
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
3548
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
3549
+
3550
+ }
3551
+
3552
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
3553
+ $.ui.ddmanager.prepareOffsets(this, event);
3554
+ }
3555
+
3556
+ //Regenerate the absolute position used for position checks
3557
+ this.positionAbs = this._convertPositionTo("absolute");
3558
+
3559
+ //Set the helper position
3560
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
3561
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
3562
+
3563
+ //Rearrange
3564
+ for (var i = this.items.length - 1; i >= 0; i--) {
3565
+
3566
+ //Cache variables and intersection, continue if no intersection
3567
+ var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
3568
+ if (!intersection) continue;
3569
+
3570
+ if(itemElement != this.currentItem[0] //cannot intersect with itself
3571
+ && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
3572
+ && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
3573
+ && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
3574
+ //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
3575
+ ) {
3576
+
3577
+ this.direction = intersection == 1 ? "down" : "up";
3578
+
3579
+ if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
3580
+ this._rearrange(event, item);
3581
+ } else {
3582
+ break;
3583
+ }
3584
+
3585
+ this._trigger("change", event, this._uiHash());
3586
+ break;
3587
+ }
3588
+ }
3589
+
3590
+ //Post events to containers
3591
+ this._contactContainers(event);
3592
+
3593
+ //Interconnect with droppables
3594
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
3595
+
3596
+ //Call callbacks
3597
+ this._trigger('sort', event, this._uiHash());
3598
+
3599
+ this.lastPositionAbs = this.positionAbs;
3600
+ return false;
3601
+
3602
+ },
3603
+
3604
+ _mouseStop: function(event, noPropagation) {
3605
+
3606
+ if(!event) return;
3607
+
3608
+ //If we are using droppables, inform the manager about the drop
3609
+ if ($.ui.ddmanager && !this.options.dropBehaviour)
3610
+ $.ui.ddmanager.drop(this, event);
3611
+
3612
+ if(this.options.revert) {
3613
+ var self = this;
3614
+ var cur = self.placeholder.offset();
3615
+
3616
+ self.reverting = true;
3617
+
3618
+ $(this.helper).animate({
3619
+ left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
3620
+ top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
3621
+ }, parseInt(this.options.revert, 10) || 500, function() {
3622
+ self._clear(event);
3623
+ });
3624
+ } else {
3625
+ this._clear(event, noPropagation);
3626
+ }
3627
+
3628
+ return false;
3629
+
3630
+ },
3631
+
3632
+ cancel: function() {
3633
+
3634
+ var self = this;
3635
+
3636
+ if(this.dragging) {
3637
+
3638
+ this._mouseUp({ target: null });
3639
+
3640
+ if(this.options.helper == "original")
3641
+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
3642
+ else
3643
+ this.currentItem.show();
3644
+
3645
+ //Post deactivating events to containers
3646
+ for (var i = this.containers.length - 1; i >= 0; i--){
3647
+ this.containers[i]._trigger("deactivate", null, self._uiHash(this));
3648
+ if(this.containers[i].containerCache.over) {
3649
+ this.containers[i]._trigger("out", null, self._uiHash(this));
3650
+ this.containers[i].containerCache.over = 0;
3651
+ }
3652
+ }
3653
+
3654
+ }
3655
+
3656
+ if (this.placeholder) {
3657
+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
3658
+ if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
3659
+ if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
3660
+
3661
+ $.extend(this, {
3662
+ helper: null,
3663
+ dragging: false,
3664
+ reverting: false,
3665
+ _noFinalSort: null
3666
+ });
3667
+
3668
+ if(this.domPosition.prev) {
3669
+ $(this.domPosition.prev).after(this.currentItem);
3670
+ } else {
3671
+ $(this.domPosition.parent).prepend(this.currentItem);
3672
+ }
3673
+ }
3674
+
3675
+ return this;
3676
+
3677
+ },
3678
+
3679
+ serialize: function(o) {
3680
+
3681
+ var items = this._getItemsAsjQuery(o && o.connected);
3682
+ var str = []; o = o || {};
3683
+
3684
+ $(items).each(function() {
3685
+ var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
3686
+ if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
3687
+ });
3688
+
3689
+ if(!str.length && o.key) {
3690
+ str.push(o.key + '=');
3691
+ }
3692
+
3693
+ return str.join('&');
3694
+
3695
+ },
3696
+
3697
+ toArray: function(o) {
3698
+
3699
+ var items = this._getItemsAsjQuery(o && o.connected);
3700
+ var ret = []; o = o || {};
3701
+
3702
+ items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
3703
+ return ret;
3704
+
3705
+ },
3706
+
3707
+ /* Be careful with the following core functions */
3708
+ _intersectsWith: function(item) {
3709
+
3710
+ var x1 = this.positionAbs.left,
3711
+ x2 = x1 + this.helperProportions.width,
3712
+ y1 = this.positionAbs.top,
3713
+ y2 = y1 + this.helperProportions.height;
3714
+
3715
+ var l = item.left,
3716
+ r = l + item.width,
3717
+ t = item.top,
3718
+ b = t + item.height;
3719
+
3720
+ var dyClick = this.offset.click.top,
3721
+ dxClick = this.offset.click.left;
3722
+
3723
+ var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
3724
+
3725
+ if( this.options.tolerance == "pointer"
3726
+ || this.options.forcePointerForContainers
3727
+ || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
3728
+ ) {
3729
+ return isOverElement;
3730
+ } else {
3731
+
3732
+ return (l < x1 + (this.helperProportions.width / 2) // Right Half
3733
+ && x2 - (this.helperProportions.width / 2) < r // Left Half
3734
+ && t < y1 + (this.helperProportions.height / 2) // Bottom Half
3735
+ && y2 - (this.helperProportions.height / 2) < b ); // Top Half
3736
+
3737
+ }
3738
+ },
3739
+
3740
+ _intersectsWithPointer: function(item) {
3741
+
3742
+ var isOverElementHeight = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
3743
+ isOverElementWidth = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
3744
+ isOverElement = isOverElementHeight && isOverElementWidth,
3745
+ verticalDirection = this._getDragVerticalDirection(),
3746
+ horizontalDirection = this._getDragHorizontalDirection();
3747
+
3748
+ if (!isOverElement)
3749
+ return false;
3750
+
3751
+ return this.floating ?
3752
+ ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
3753
+ : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
3754
+
3755
+ },
3756
+
3757
+ _intersectsWithSides: function(item) {
3758
+
3759
+ var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
3760
+ isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
3761
+ verticalDirection = this._getDragVerticalDirection(),
3762
+ horizontalDirection = this._getDragHorizontalDirection();
3763
+
3764
+ if (this.floating && horizontalDirection) {
3765
+ return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
3766
+ } else {
3767
+ return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
3768
+ }
3769
+
3770
+ },
3771
+
3772
+ _getDragVerticalDirection: function() {
3773
+ var delta = this.positionAbs.top - this.lastPositionAbs.top;
3774
+ return delta != 0 && (delta > 0 ? "down" : "up");
3775
+ },
3776
+
3777
+ _getDragHorizontalDirection: function() {
3778
+ var delta = this.positionAbs.left - this.lastPositionAbs.left;
3779
+ return delta != 0 && (delta > 0 ? "right" : "left");
3780
+ },
3781
+
3782
+ refresh: function(event) {
3783
+ this._refreshItems(event);
3784
+ this.refreshPositions();
3785
+ return this;
3786
+ },
3787
+
3788
+ _connectWith: function() {
3789
+ var options = this.options;
3790
+ return options.connectWith.constructor == String
3791
+ ? [options.connectWith]
3792
+ : options.connectWith;
3793
+ },
3794
+
3795
+ _getItemsAsjQuery: function(connected) {
3796
+
3797
+ var self = this;
3798
+ var items = [];
3799
+ var queries = [];
3800
+ var connectWith = this._connectWith();
3801
+
3802
+ if(connectWith && connected) {
3803
+ for (var i = connectWith.length - 1; i >= 0; i--){
3804
+ var cur = $(connectWith[i]);
3805
+ for (var j = cur.length - 1; j >= 0; j--){
3806
+ var inst = $.data(cur[j], this.widgetName);
3807
+ if(inst && inst != this && !inst.options.disabled) {
3808
+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]);
3809
+ }
3810
+ };
3811
+ };
3812
+ }
3813
+
3814
+ queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]);
3815
+
3816
+ for (var i = queries.length - 1; i >= 0; i--){
3817
+ queries[i][0].each(function() {
3818
+ items.push(this);
3819
+ });
3820
+ };
3821
+
3822
+ return $(items);
3823
+
3824
+ },
3825
+
3826
+ _removeCurrentsFromItems: function() {
3827
+
3828
+ var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
3829
+
3830
+ for (var i=0; i < this.items.length; i++) {
3831
+
3832
+ for (var j=0; j < list.length; j++) {
3833
+ if(list[j] == this.items[i].item[0])
3834
+ this.items.splice(i,1);
3835
+ };
3836
+
3837
+ };
3838
+
3839
+ },
3840
+
3841
+ _refreshItems: function(event) {
3842
+
3843
+ this.items = [];
3844
+ this.containers = [this];
3845
+ var items = this.items;
3846
+ var self = this;
3847
+ var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
3848
+ var connectWith = this._connectWith();
3849
+
3850
+ if(connectWith) {
3851
+ for (var i = connectWith.length - 1; i >= 0; i--){
3852
+ var cur = $(connectWith[i]);
3853
+ for (var j = cur.length - 1; j >= 0; j--){
3854
+ var inst = $.data(cur[j], this.widgetName);
3855
+ if(inst && inst != this && !inst.options.disabled) {
3856
+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
3857
+ this.containers.push(inst);
3858
+ }
3859
+ };
3860
+ };
3861
+ }
3862
+
3863
+ for (var i = queries.length - 1; i >= 0; i--) {
3864
+ var targetData = queries[i][1];
3865
+ var _queries = queries[i][0];
3866
+
3867
+ for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
3868
+ var item = $(_queries[j]);
3869
+
3870
+ item.data(this.widgetName + '-item', targetData); // Data for target checking (mouse manager)
3871
+
3872
+ items.push({
3873
+ item: item,
3874
+ instance: targetData,
3875
+ width: 0, height: 0,
3876
+ left: 0, top: 0
3877
+ });
3878
+ };
3879
+ };
3880
+
3881
+ },
3882
+
3883
+ refreshPositions: function(fast) {
3884
+
3885
+ //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
3886
+ if(this.offsetParent && this.helper) {
3887
+ this.offset.parent = this._getParentOffset();
3888
+ }
3889
+
3890
+ for (var i = this.items.length - 1; i >= 0; i--){
3891
+ var item = this.items[i];
3892
+
3893
+ //We ignore calculating positions of all connected containers when we're not over them
3894
+ if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0])
3895
+ continue;
3896
+
3897
+ var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
3898
+
3899
+ if (!fast) {
3900
+ item.width = t.outerWidth();
3901
+ item.height = t.outerHeight();
3902
+ }
3903
+
3904
+ var p = t.offset();
3905
+ item.left = p.left;
3906
+ item.top = p.top;
3907
+ };
3908
+
3909
+ if(this.options.custom && this.options.custom.refreshContainers) {
3910
+ this.options.custom.refreshContainers.call(this);
3911
+ } else {
3912
+ for (var i = this.containers.length - 1; i >= 0; i--){
3913
+ var p = this.containers[i].element.offset();
3914
+ this.containers[i].containerCache.left = p.left;
3915
+ this.containers[i].containerCache.top = p.top;
3916
+ this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
3917
+ this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
3918
+ };
3919
+ }
3920
+
3921
+ return this;
3922
+ },
3923
+
3924
+ _createPlaceholder: function(that) {
3925
+
3926
+ var self = that || this, o = self.options;
3927
+
3928
+ if(!o.placeholder || o.placeholder.constructor == String) {
3929
+ var className = o.placeholder;
3930
+ o.placeholder = {
3931
+ element: function() {
3932
+
3933
+ var el = $(document.createElement(self.currentItem[0].nodeName))
3934
+ .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
3935
+ .removeClass("ui-sortable-helper")[0];
3936
+
3937
+ if(!className)
3938
+ el.style.visibility = "hidden";
3939
+
3940
+ return el;
3941
+ },
3942
+ update: function(container, p) {
3943
+
3944
+ // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
3945
+ // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
3946
+ if(className && !o.forcePlaceholderSize) return;
3947
+
3948
+ //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
3949
+ if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); };
3950
+ if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); };
3951
+ }
3952
+ };
3953
+ }
3954
+
3955
+ //Create the placeholder
3956
+ self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem));
3957
+
3958
+ //Append it after the actual current item
3959
+ self.currentItem.after(self.placeholder);
3960
+
3961
+ //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
3962
+ o.placeholder.update(self, self.placeholder);
3963
+
3964
+ },
3965
+
3966
+ _contactContainers: function(event) {
3967
+
3968
+ // get innermost container that intersects with item
3969
+ var innermostContainer = null, innermostIndex = null;
3970
+
3971
+
3972
+ for (var i = this.containers.length - 1; i >= 0; i--){
3973
+
3974
+ // never consider a container that's located within the item itself
3975
+ if($.ui.contains(this.currentItem[0], this.containers[i].element[0]))
3976
+ continue;
3977
+
3978
+ if(this._intersectsWith(this.containers[i].containerCache)) {
3979
+
3980
+ // if we've already found a container and it's more "inner" than this, then continue
3981
+ if(innermostContainer && $.ui.contains(this.containers[i].element[0], innermostContainer.element[0]))
3982
+ continue;
3983
+
3984
+ innermostContainer = this.containers[i];
3985
+ innermostIndex = i;
3986
+
3987
+ } else {
3988
+ // container doesn't intersect. trigger "out" event if necessary
3989
+ if(this.containers[i].containerCache.over) {
3990
+ this.containers[i]._trigger("out", event, this._uiHash(this));
3991
+ this.containers[i].containerCache.over = 0;
3992
+ }
3993
+ }
3994
+
3995
+ }
3996
+
3997
+ // if no intersecting containers found, return
3998
+ if(!innermostContainer) return;
3999
+
4000
+ // move the item into the container if it's not there already
4001
+ if(this.containers.length === 1) {
4002
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4003
+ this.containers[innermostIndex].containerCache.over = 1;
4004
+ } else if(this.currentContainer != this.containers[innermostIndex]) {
4005
+
4006
+ //When entering a new container, we will find the item with the least distance and append our item near it
4007
+ var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top'];
4008
+ for (var j = this.items.length - 1; j >= 0; j--) {
4009
+ if(!$.ui.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
4010
+ var cur = this.items[j][this.containers[innermostIndex].floating ? 'left' : 'top'];
4011
+ if(Math.abs(cur - base) < dist) {
4012
+ dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
4013
+ }
4014
+ }
4015
+
4016
+ if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
4017
+ return;
4018
+
4019
+ this.currentContainer = this.containers[innermostIndex];
4020
+ itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
4021
+ this._trigger("change", event, this._uiHash());
4022
+ this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
4023
+
4024
+ //Update the placeholder
4025
+ this.options.placeholder.update(this.currentContainer, this.placeholder);
4026
+
4027
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4028
+ this.containers[innermostIndex].containerCache.over = 1;
4029
+ }
4030
+
4031
+
4032
+ },
4033
+
4034
+ _createHelper: function(event) {
4035
+
4036
+ var o = this.options;
4037
+ var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
4038
+
4039
+ if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
4040
+ $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
4041
+
4042
+ if(helper[0] == this.currentItem[0])
4043
+ this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
4044
+
4045
+ if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
4046
+ if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
4047
+
4048
+ return helper;
4049
+
4050
+ },
4051
+
4052
+ _adjustOffsetFromHelper: function(obj) {
4053
+ if (typeof obj == 'string') {
4054
+ obj = obj.split(' ');
4055
+ }
4056
+ if ($.isArray(obj)) {
4057
+ obj = {left: +obj[0], top: +obj[1] || 0};
4058
+ }
4059
+ if ('left' in obj) {
4060
+ this.offset.click.left = obj.left + this.margins.left;
4061
+ }
4062
+ if ('right' in obj) {
4063
+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
4064
+ }
4065
+ if ('top' in obj) {
4066
+ this.offset.click.top = obj.top + this.margins.top;
4067
+ }
4068
+ if ('bottom' in obj) {
4069
+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
4070
+ }
4071
+ },
4072
+
4073
+ _getParentOffset: function() {
4074
+
4075
+
4076
+ //Get the offsetParent and cache its position
4077
+ this.offsetParent = this.helper.offsetParent();
4078
+ var po = this.offsetParent.offset();
4079
+
4080
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
4081
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
4082
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
4083
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
4084
+ if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
4085
+ po.left += this.scrollParent.scrollLeft();
4086
+ po.top += this.scrollParent.scrollTop();
4087
+ }
4088
+
4089
+ if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
4090
+ || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
4091
+ po = { top: 0, left: 0 };
4092
+
4093
+ return {
4094
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
4095
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
4096
+ };
4097
+
4098
+ },
4099
+
4100
+ _getRelativeOffset: function() {
4101
+
4102
+ if(this.cssPosition == "relative") {
4103
+ var p = this.currentItem.position();
4104
+ return {
4105
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
4106
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
4107
+ };
4108
+ } else {
4109
+ return { top: 0, left: 0 };
4110
+ }
4111
+
4112
+ },
4113
+
4114
+ _cacheMargins: function() {
4115
+ this.margins = {
4116
+ left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
4117
+ top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
4118
+ };
4119
+ },
4120
+
4121
+ _cacheHelperProportions: function() {
4122
+ this.helperProportions = {
4123
+ width: this.helper.outerWidth(),
4124
+ height: this.helper.outerHeight()
4125
+ };
4126
+ },
4127
+
4128
+ _setContainment: function() {
4129
+
4130
+ var o = this.options;
4131
+ if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
4132
+ if(o.containment == 'document' || o.containment == 'window') this.containment = [
4133
+ 0 - this.offset.relative.left - this.offset.parent.left,
4134
+ 0 - this.offset.relative.top - this.offset.parent.top,
4135
+ $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
4136
+ ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4137
+ ];
4138
+
4139
+ if(!(/^(document|window|parent)$/).test(o.containment)) {
4140
+ var ce = $(o.containment)[0];
4141
+ var co = $(o.containment).offset();
4142
+ var over = ($(ce).css("overflow") != 'hidden');
4143
+
4144
+ this.containment = [
4145
+ co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
4146
+ co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
4147
+ co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
4148
+ co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
4149
+ ];
4150
+ }
4151
+
4152
+ },
4153
+
4154
+ _convertPositionTo: function(d, pos) {
4155
+
4156
+ if(!pos) pos = this.position;
4157
+ var mod = d == "absolute" ? 1 : -1;
4158
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4159
+
4160
+ return {
4161
+ top: (
4162
+ pos.top // The absolute mouse position
4163
+ + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
4164
+ + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
4165
+ - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
4166
+ ),
4167
+ left: (
4168
+ pos.left // The absolute mouse position
4169
+ + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
4170
+ + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
4171
+ - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
4172
+ )
4173
+ };
4174
+
4175
+ },
4176
+
4177
+ _generatePosition: function(event) {
4178
+
4179
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4180
+
4181
+ // This is another very weird special case that only happens for relative elements:
4182
+ // 1. If the css position is relative
4183
+ // 2. and the scroll parent is the document or similar to the offset parent
4184
+ // we have to refresh the relative offset during the scroll so there are no jumps
4185
+ if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
4186
+ this.offset.relative = this._getRelativeOffset();
4187
+ }
4188
+
4189
+ var pageX = event.pageX;
4190
+ var pageY = event.pageY;
4191
+
4192
+ /*
4193
+ * - Position constraining -
4194
+ * Constrain the position to a mix of grid, containment.
4195
+ */
4196
+
4197
+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
4198
+
4199
+ if(this.containment) {
4200
+ if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
4201
+ if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
4202
+ if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
4203
+ if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
4204
+ }
4205
+
4206
+ if(o.grid) {
4207
+ var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
4208
+ pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
4209
+
4210
+ var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
4211
+ pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
4212
+ }
4213
+
4214
+ }
4215
+
4216
+ return {
4217
+ top: (
4218
+ pageY // The absolute mouse position
4219
+ - this.offset.click.top // Click offset (relative to the element)
4220
+ - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
4221
+ - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
4222
+ + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
4223
+ ),
4224
+ left: (
4225
+ pageX // The absolute mouse position
4226
+ - this.offset.click.left // Click offset (relative to the element)
4227
+ - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
4228
+ - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
4229
+ + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
4230
+ )
4231
+ };
4232
+
4233
+ },
4234
+
4235
+ _rearrange: function(event, i, a, hardRefresh) {
4236
+
4237
+ a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
4238
+
4239
+ //Various things done here to improve the performance:
4240
+ // 1. we create a setTimeout, that calls refreshPositions
4241
+ // 2. on the instance, we have a counter variable, that get's higher after every append
4242
+ // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
4243
+ // 4. this lets only the last addition to the timeout stack through
4244
+ this.counter = this.counter ? ++this.counter : 1;
4245
+ var self = this, counter = this.counter;
4246
+
4247
+ window.setTimeout(function() {
4248
+ if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
4249
+ },0);
4250
+
4251
+ },
4252
+
4253
+ _clear: function(event, noPropagation) {
4254
+
4255
+ this.reverting = false;
4256
+ // We delay all events that have to be triggered to after the point where the placeholder has been removed and
4257
+ // everything else normalized again
4258
+ var delayedTriggers = [], self = this;
4259
+
4260
+ // We first have to update the dom position of the actual currentItem
4261
+ // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
4262
+ if(!this._noFinalSort && this.currentItem.parent().length) this.placeholder.before(this.currentItem);
4263
+ this._noFinalSort = null;
4264
+
4265
+ if(this.helper[0] == this.currentItem[0]) {
4266
+ for(var i in this._storedCSS) {
4267
+ if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = '';
4268
+ }
4269
+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4270
+ } else {
4271
+ this.currentItem.show();
4272
+ }
4273
+
4274
+ if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
4275
+ if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
4276
+ if(!$.ui.contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element
4277
+ if(!noPropagation) delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
4278
+ for (var i = this.containers.length - 1; i >= 0; i--){
4279
+ if($.ui.contains(this.containers[i].element[0], this.currentItem[0]) && !noPropagation) {
4280
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4281
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4282
+ }
4283
+ };
4284
+ };
4285
+
4286
+ //Post events to containers
4287
+ for (var i = this.containers.length - 1; i >= 0; i--){
4288
+ if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4289
+ if(this.containers[i].containerCache.over) {
4290
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4291
+ this.containers[i].containerCache.over = 0;
4292
+ }
4293
+ }
4294
+
4295
+ //Do what was originally in plugins
4296
+ if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor
4297
+ if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity
4298
+ if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index
4299
+
4300
+ this.dragging = false;
4301
+ if(this.cancelHelperRemoval) {
4302
+ if(!noPropagation) {
4303
+ this._trigger("beforeStop", event, this._uiHash());
4304
+ for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
4305
+ this._trigger("stop", event, this._uiHash());
4306
+ }
4307
+ return false;
4308
+ }
4309
+
4310
+ if(!noPropagation) this._trigger("beforeStop", event, this._uiHash());
4311
+
4312
+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4313
+ this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4314
+
4315
+ if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null;
4316
+
4317
+ if(!noPropagation) {
4318
+ for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
4319
+ this._trigger("stop", event, this._uiHash());
4320
+ }
4321
+
4322
+ this.fromOutside = false;
4323
+ return true;
4324
+
4325
+ },
4326
+
4327
+ _trigger: function() {
4328
+ if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
4329
+ this.cancel();
4330
+ }
4331
+ },
4332
+
4333
+ _uiHash: function(inst) {
4334
+ var self = inst || this;
4335
+ return {
4336
+ helper: self.helper,
4337
+ placeholder: self.placeholder || $([]),
4338
+ position: self.position,
4339
+ originalPosition: self.originalPosition,
4340
+ offset: self.positionAbs,
4341
+ item: self.currentItem,
4342
+ sender: inst ? inst.element : null
4343
+ };
4344
+ }
4345
+
4346
+ });
4347
+
4348
+ $.extend($.ui.sortable, {
4349
+ version: "1.8.17"
4350
+ });
4351
+
4352
+ })(jQuery);
4353
+ /*
4354
+ * jQuery UI Accordion 1.8.17
4355
+ *
4356
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
4357
+ * Dual licensed under the MIT or GPL Version 2 licenses.
4358
+ * http://jquery.org/license
4359
+ *
4360
+ * http://docs.jquery.com/UI/Accordion
4361
+ *
4362
+ * Depends:
4363
+ * jquery.ui.core.js
4364
+ * jquery.ui.widget.js
4365
+ */
4366
+ (function( $, undefined ) {
4367
+
4368
+ $.widget( "ui.accordion", {
4369
+ options: {
4370
+ active: 0,
4371
+ animated: "slide",
4372
+ autoHeight: true,
4373
+ clearStyle: false,
4374
+ collapsible: false,
4375
+ event: "click",
4376
+ fillSpace: false,
4377
+ header: "> li > :first-child,> :not(li):even",
4378
+ icons: {
4379
+ header: "ui-icon-triangle-1-e",
4380
+ headerSelected: "ui-icon-triangle-1-s"
4381
+ },
4382
+ navigation: false,
4383
+ navigationFilter: function() {
4384
+ return this.href.toLowerCase() === location.href.toLowerCase();
4385
+ }
4386
+ },
4387
+
4388
+ _create: function() {
4389
+ var self = this,
4390
+ options = self.options;
4391
+
4392
+ self.running = 0;
4393
+
4394
+ self.element
4395
+ .addClass( "ui-accordion ui-widget ui-helper-reset" )
4396
+ // in lack of child-selectors in CSS
4397
+ // we need to mark top-LIs in a UL-accordion for some IE-fix
4398
+ .children( "li" )
4399
+ .addClass( "ui-accordion-li-fix" );
4400
+
4401
+ self.headers = self.element.find( options.header )
4402
+ .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" )
4403
+ .bind( "mouseenter.accordion", function() {
4404
+ if ( options.disabled ) {
4405
+ return;
4406
+ }
4407
+ $( this ).addClass( "ui-state-hover" );
4408
+ })
4409
+ .bind( "mouseleave.accordion", function() {
4410
+ if ( options.disabled ) {
4411
+ return;
4412
+ }
4413
+ $( this ).removeClass( "ui-state-hover" );
4414
+ })
4415
+ .bind( "focus.accordion", function() {
4416
+ if ( options.disabled ) {
4417
+ return;
4418
+ }
4419
+ $( this ).addClass( "ui-state-focus" );
4420
+ })
4421
+ .bind( "blur.accordion", function() {
4422
+ if ( options.disabled ) {
4423
+ return;
4424
+ }
4425
+ $( this ).removeClass( "ui-state-focus" );
4426
+ });
4427
+
4428
+ self.headers.next()
4429
+ .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" );
4430
+
4431
+ if ( options.navigation ) {
4432
+ var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 );
4433
+ if ( current.length ) {
4434
+ var header = current.closest( ".ui-accordion-header" );
4435
+ if ( header.length ) {
4436
+ // anchor within header
4437
+ self.active = header;
4438
+ } else {
4439
+ // anchor within content
4440
+ self.active = current.closest( ".ui-accordion-content" ).prev();
4441
+ }
4442
+ }
4443
+ }
4444
+
4445
+ self.active = self._findActive( self.active || options.active )
4446
+ .addClass( "ui-state-default ui-state-active" )
4447
+ .toggleClass( "ui-corner-all" )
4448
+ .toggleClass( "ui-corner-top" );
4449
+ self.active.next().addClass( "ui-accordion-content-active" );
4450
+
4451
+ self._createIcons();
4452
+ self.resize();
4453
+
4454
+ // ARIA
4455
+ self.element.attr( "role", "tablist" );
4456
+
4457
+ self.headers
4458
+ .attr( "role", "tab" )
4459
+ .bind( "keydown.accordion", function( event ) {
4460
+ return self._keydown( event );
4461
+ })
4462
+ .next()
4463
+ .attr( "role", "tabpanel" );
4464
+
4465
+ self.headers
4466
+ .not( self.active || "" )
4467
+ .attr({
4468
+ "aria-expanded": "false",
4469
+ "aria-selected": "false",
4470
+ tabIndex: -1
4471
+ })
4472
+ .next()
4473
+ .hide();
4474
+
4475
+ // make sure at least one header is in the tab order
4476
+ if ( !self.active.length ) {
4477
+ self.headers.eq( 0 ).attr( "tabIndex", 0 );
4478
+ } else {
4479
+ self.active
4480
+ .attr({
4481
+ "aria-expanded": "true",
4482
+ "aria-selected": "true",
4483
+ tabIndex: 0
4484
+ });
4485
+ }
4486
+
4487
+ // only need links in tab order for Safari
4488
+ if ( !$.browser.safari ) {
4489
+ self.headers.find( "a" ).attr( "tabIndex", -1 );
4490
+ }
4491
+
4492
+ if ( options.event ) {
4493
+ self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) {
4494
+ self._clickHandler.call( self, event, this );
4495
+ event.preventDefault();
4496
+ });
4497
+ }
4498
+ },
4499
+
4500
+ _createIcons: function() {
4501
+ var options = this.options;
4502
+ if ( options.icons ) {
4503
+ $( "<span></span>" )
4504
+ .addClass( "ui-icon " + options.icons.header )
4505
+ .prependTo( this.headers );
4506
+ this.active.children( ".ui-icon" )
4507
+ .toggleClass(options.icons.header)
4508
+ .toggleClass(options.icons.headerSelected);
4509
+ this.element.addClass( "ui-accordion-icons" );
4510
+ }
4511
+ },
4512
+
4513
+ _destroyIcons: function() {
4514
+ this.headers.children( ".ui-icon" ).remove();
4515
+ this.element.removeClass( "ui-accordion-icons" );
4516
+ },
4517
+
4518
+ destroy: function() {
4519
+ var options = this.options;
4520
+
4521
+ this.element
4522
+ .removeClass( "ui-accordion ui-widget ui-helper-reset" )
4523
+ .removeAttr( "role" );
4524
+
4525
+ this.headers
4526
+ .unbind( ".accordion" )
4527
+ .removeClass( "ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
4528
+ .removeAttr( "role" )
4529
+ .removeAttr( "aria-expanded" )
4530
+ .removeAttr( "aria-selected" )
4531
+ .removeAttr( "tabIndex" );
4532
+
4533
+ this.headers.find( "a" ).removeAttr( "tabIndex" );
4534
+ this._destroyIcons();
4535
+ var contents = this.headers.next()
4536
+ .css( "display", "" )
4537
+ .removeAttr( "role" )
4538
+ .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled" );
4539
+ if ( options.autoHeight || options.fillHeight ) {
4540
+ contents.css( "height", "" );
4541
+ }
4542
+
4543
+ return $.Widget.prototype.destroy.call( this );
4544
+ },
4545
+
4546
+ _setOption: function( key, value ) {
4547
+ $.Widget.prototype._setOption.apply( this, arguments );
4548
+
4549
+ if ( key == "active" ) {
4550
+ this.activate( value );
4551
+ }
4552
+ if ( key == "icons" ) {
4553
+ this._destroyIcons();
4554
+ if ( value ) {
4555
+ this._createIcons();
4556
+ }
4557
+ }
4558
+ // #5332 - opacity doesn't cascade to positioned elements in IE
4559
+ // so we need to add the disabled class to the headers and panels
4560
+ if ( key == "disabled" ) {
4561
+ this.headers.add(this.headers.next())
4562
+ [ value ? "addClass" : "removeClass" ](
4563
+ "ui-accordion-disabled ui-state-disabled" );
4564
+ }
4565
+ },
4566
+
4567
+ _keydown: function( event ) {
4568
+ if ( this.options.disabled || event.altKey || event.ctrlKey ) {
4569
+ return;
4570
+ }
4571
+
4572
+ var keyCode = $.ui.keyCode,
4573
+ length = this.headers.length,
4574
+ currentIndex = this.headers.index( event.target ),
4575
+ toFocus = false;
4576
+
4577
+ switch ( event.keyCode ) {
4578
+ case keyCode.RIGHT:
4579
+ case keyCode.DOWN:
4580
+ toFocus = this.headers[ ( currentIndex + 1 ) % length ];
4581
+ break;
4582
+ case keyCode.LEFT:
4583
+ case keyCode.UP:
4584
+ toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
4585
+ break;
4586
+ case keyCode.SPACE:
4587
+ case keyCode.ENTER:
4588
+ this._clickHandler( { target: event.target }, event.target );
4589
+ event.preventDefault();
4590
+ }
4591
+
4592
+ if ( toFocus ) {
4593
+ $( event.target ).attr( "tabIndex", -1 );
4594
+ $( toFocus ).attr( "tabIndex", 0 );
4595
+ toFocus.focus();
4596
+ return false;
4597
+ }
4598
+
4599
+ return true;
4600
+ },
4601
+
4602
+ resize: function() {
4603
+ var options = this.options,
4604
+ maxHeight;
4605
+
4606
+ if ( options.fillSpace ) {
4607
+ if ( $.browser.msie ) {
4608
+ var defOverflow = this.element.parent().css( "overflow" );
4609
+ this.element.parent().css( "overflow", "hidden");
4610
+ }
4611
+ maxHeight = this.element.parent().height();
4612
+ if ($.browser.msie) {
4613
+ this.element.parent().css( "overflow", defOverflow );
4614
+ }
4615
+
4616
+ this.headers.each(function() {
4617
+ maxHeight -= $( this ).outerHeight( true );
4618
+ });
4619
+
4620
+ this.headers.next()
4621
+ .each(function() {
4622
+ $( this ).height( Math.max( 0, maxHeight -
4623
+ $( this ).innerHeight() + $( this ).height() ) );
4624
+ })
4625
+ .css( "overflow", "auto" );
4626
+ } else if ( options.autoHeight ) {
4627
+ maxHeight = 0;
4628
+ this.headers.next()
4629
+ .each(function() {
4630
+ maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
4631
+ })
4632
+ .height( maxHeight );
4633
+ }
4634
+
4635
+ return this;
4636
+ },
4637
+
4638
+ activate: function( index ) {
4639
+ // TODO this gets called on init, changing the option without an explicit call for that
4640
+ this.options.active = index;
4641
+ // call clickHandler with custom event
4642
+ var active = this._findActive( index )[ 0 ];
4643
+ this._clickHandler( { target: active }, active );
4644
+
4645
+ return this;
4646
+ },
4647
+
4648
+ _findActive: function( selector ) {
4649
+ return selector
4650
+ ? typeof selector === "number"
4651
+ ? this.headers.filter( ":eq(" + selector + ")" )
4652
+ : this.headers.not( this.headers.not( selector ) )
4653
+ : selector === false
4654
+ ? $( [] )
4655
+ : this.headers.filter( ":eq(0)" );
4656
+ },
4657
+
4658
+ // TODO isn't event.target enough? why the separate target argument?
4659
+ _clickHandler: function( event, target ) {
4660
+ var options = this.options;
4661
+ if ( options.disabled ) {
4662
+ return;
4663
+ }
4664
+
4665
+ // called only when using activate(false) to close all parts programmatically
4666
+ if ( !event.target ) {
4667
+ if ( !options.collapsible ) {
4668
+ return;
4669
+ }
4670
+ this.active
4671
+ .removeClass( "ui-state-active ui-corner-top" )
4672
+ .addClass( "ui-state-default ui-corner-all" )
4673
+ .children( ".ui-icon" )
4674
+ .removeClass( options.icons.headerSelected )
4675
+ .addClass( options.icons.header );
4676
+ this.active.next().addClass( "ui-accordion-content-active" );
4677
+ var toHide = this.active.next(),
4678
+ data = {
4679
+ options: options,
4680
+ newHeader: $( [] ),
4681
+ oldHeader: options.active,
4682
+ newContent: $( [] ),
4683
+ oldContent: toHide
4684
+ },
4685
+ toShow = ( this.active = $( [] ) );
4686
+ this._toggle( toShow, toHide, data );
4687
+ return;
4688
+ }
4689
+
4690
+ // get the click target
4691
+ var clicked = $( event.currentTarget || target ),
4692
+ clickedIsActive = clicked[0] === this.active[0];
4693
+
4694
+ // TODO the option is changed, is that correct?
4695
+ // TODO if it is correct, shouldn't that happen after determining that the click is valid?
4696
+ options.active = options.collapsible && clickedIsActive ?
4697
+ false :
4698
+ this.headers.index( clicked );
4699
+
4700
+ // if animations are still active, or the active header is the target, ignore click
4701
+ if ( this.running || ( !options.collapsible && clickedIsActive ) ) {
4702
+ return;
4703
+ }
4704
+
4705
+ // find elements to show and hide
4706
+ var active = this.active,
4707
+ toShow = clicked.next(),
4708
+ toHide = this.active.next(),
4709
+ data = {
4710
+ options: options,
4711
+ newHeader: clickedIsActive && options.collapsible ? $([]) : clicked,
4712
+ oldHeader: this.active,
4713
+ newContent: clickedIsActive && options.collapsible ? $([]) : toShow,
4714
+ oldContent: toHide
4715
+ },
4716
+ down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
4717
+
4718
+ // when the call to ._toggle() comes after the class changes
4719
+ // it causes a very odd bug in IE 8 (see #6720)
4720
+ this.active = clickedIsActive ? $([]) : clicked;
4721
+ this._toggle( toShow, toHide, data, clickedIsActive, down );
4722
+
4723
+ // switch classes
4724
+ active
4725
+ .removeClass( "ui-state-active ui-corner-top" )
4726
+ .addClass( "ui-state-default ui-corner-all" )
4727
+ .children( ".ui-icon" )
4728
+ .removeClass( options.icons.headerSelected )
4729
+ .addClass( options.icons.header );
4730
+ if ( !clickedIsActive ) {
4731
+ clicked
4732
+ .removeClass( "ui-state-default ui-corner-all" )
4733
+ .addClass( "ui-state-active ui-corner-top" )
4734
+ .children( ".ui-icon" )
4735
+ .removeClass( options.icons.header )
4736
+ .addClass( options.icons.headerSelected );
4737
+ clicked
4738
+ .next()
4739
+ .addClass( "ui-accordion-content-active" );
4740
+ }
4741
+
4742
+ return;
4743
+ },
4744
+
4745
+ _toggle: function( toShow, toHide, data, clickedIsActive, down ) {
4746
+ var self = this,
4747
+ options = self.options;
4748
+
4749
+ self.toShow = toShow;
4750
+ self.toHide = toHide;
4751
+ self.data = data;
4752
+
4753
+ var complete = function() {
4754
+ if ( !self ) {
4755
+ return;
4756
+ }
4757
+ return self._completed.apply( self, arguments );
4758
+ };
4759
+
4760
+ // trigger changestart event
4761
+ self._trigger( "changestart", null, self.data );
4762
+
4763
+ // count elements to animate
4764
+ self.running = toHide.size() === 0 ? toShow.size() : toHide.size();
4765
+
4766
+ if ( options.animated ) {
4767
+ var animOptions = {};
4768
+
4769
+ if ( options.collapsible && clickedIsActive ) {
4770
+ animOptions = {
4771
+ toShow: $( [] ),
4772
+ toHide: toHide,
4773
+ complete: complete,
4774
+ down: down,
4775
+ autoHeight: options.autoHeight || options.fillSpace
4776
+ };
4777
+ } else {
4778
+ animOptions = {
4779
+ toShow: toShow,
4780
+ toHide: toHide,
4781
+ complete: complete,
4782
+ down: down,
4783
+ autoHeight: options.autoHeight || options.fillSpace
4784
+ };
4785
+ }
4786
+
4787
+ if ( !options.proxied ) {
4788
+ options.proxied = options.animated;
4789
+ }
4790
+
4791
+ if ( !options.proxiedDuration ) {
4792
+ options.proxiedDuration = options.duration;
4793
+ }
4794
+
4795
+ options.animated = $.isFunction( options.proxied ) ?
4796
+ options.proxied( animOptions ) :
4797
+ options.proxied;
4798
+
4799
+ options.duration = $.isFunction( options.proxiedDuration ) ?
4800
+ options.proxiedDuration( animOptions ) :
4801
+ options.proxiedDuration;
4802
+
4803
+ var animations = $.ui.accordion.animations,
4804
+ duration = options.duration,
4805
+ easing = options.animated;
4806
+
4807
+ if ( easing && !animations[ easing ] && !$.easing[ easing ] ) {
4808
+ easing = "slide";
4809
+ }
4810
+ if ( !animations[ easing ] ) {
4811
+ animations[ easing ] = function( options ) {
4812
+ this.slide( options, {
4813
+ easing: easing,
4814
+ duration: duration || 700
4815
+ });
4816
+ };
4817
+ }
4818
+
4819
+ animations[ easing ]( animOptions );
4820
+ } else {
4821
+ if ( options.collapsible && clickedIsActive ) {
4822
+ toShow.toggle();
4823
+ } else {
4824
+ toHide.hide();
4825
+ toShow.show();
4826
+ }
4827
+
4828
+ complete( true );
4829
+ }
4830
+
4831
+ // TODO assert that the blur and focus triggers are really necessary, remove otherwise
4832
+ toHide.prev()
4833
+ .attr({
4834
+ "aria-expanded": "false",
4835
+ "aria-selected": "false",
4836
+ tabIndex: -1
4837
+ })
4838
+ .blur();
4839
+ toShow.prev()
4840
+ .attr({
4841
+ "aria-expanded": "true",
4842
+ "aria-selected": "true",
4843
+ tabIndex: 0
4844
+ })
4845
+ .focus();
4846
+ },
4847
+
4848
+ _completed: function( cancel ) {
4849
+ this.running = cancel ? 0 : --this.running;
4850
+ if ( this.running ) {
4851
+ return;
4852
+ }
4853
+
4854
+ if ( this.options.clearStyle ) {
4855
+ this.toShow.add( this.toHide ).css({
4856
+ height: "",
4857
+ overflow: ""
4858
+ });
4859
+ }
4860
+
4861
+ // other classes are removed before the animation; this one needs to stay until completed
4862
+ this.toHide.removeClass( "ui-accordion-content-active" );
4863
+ // Work around for rendering bug in IE (#5421)
4864
+ if ( this.toHide.length ) {
4865
+ this.toHide.parent()[0].className = this.toHide.parent()[0].className;
4866
+ }
4867
+
4868
+ this._trigger( "change", null, this.data );
4869
+ }
4870
+ });
4871
+
4872
+ $.extend( $.ui.accordion, {
4873
+ version: "1.8.17",
4874
+ animations: {
4875
+ slide: function( options, additions ) {
4876
+ options = $.extend({
4877
+ easing: "swing",
4878
+ duration: 300
4879
+ }, options, additions );
4880
+ if ( !options.toHide.size() ) {
4881
+ options.toShow.animate({
4882
+ height: "show",
4883
+ paddingTop: "show",
4884
+ paddingBottom: "show"
4885
+ }, options );
4886
+ return;
4887
+ }
4888
+ if ( !options.toShow.size() ) {
4889
+ options.toHide.animate({
4890
+ height: "hide",
4891
+ paddingTop: "hide",
4892
+ paddingBottom: "hide"
4893
+ }, options );
4894
+ return;
4895
+ }
4896
+ var overflow = options.toShow.css( "overflow" ),
4897
+ percentDone = 0,
4898
+ showProps = {},
4899
+ hideProps = {},
4900
+ fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
4901
+ originalWidth;
4902
+ // fix width before calculating height of hidden element
4903
+ var s = options.toShow;
4904
+ originalWidth = s[0].style.width;
4905
+ s.width( s.parent().width()
4906
+ - parseFloat( s.css( "paddingLeft" ) )
4907
+ - parseFloat( s.css( "paddingRight" ) )
4908
+ - ( parseFloat( s.css( "borderLeftWidth" ) ) || 0 )
4909
+ - ( parseFloat( s.css( "borderRightWidth" ) ) || 0 ) );
4910
+
4911
+ $.each( fxAttrs, function( i, prop ) {
4912
+ hideProps[ prop ] = "hide";
4913
+
4914
+ var parts = ( "" + $.css( options.toShow[0], prop ) ).match( /^([\d+-.]+)(.*)$/ );
4915
+ showProps[ prop ] = {
4916
+ value: parts[ 1 ],
4917
+ unit: parts[ 2 ] || "px"
4918
+ };
4919
+ });
4920
+ options.toShow.css({ height: 0, overflow: "hidden" }).show();
4921
+ options.toHide
4922
+ .filter( ":hidden" )
4923
+ .each( options.complete )
4924
+ .end()
4925
+ .filter( ":visible" )
4926
+ .animate( hideProps, {
4927
+ step: function( now, settings ) {
4928
+ // only calculate the percent when animating height
4929
+ // IE gets very inconsistent results when animating elements
4930
+ // with small values, which is common for padding
4931
+ if ( settings.prop == "height" ) {
4932
+ percentDone = ( settings.end - settings.start === 0 ) ? 0 :
4933
+ ( settings.now - settings.start ) / ( settings.end - settings.start );
4934
+ }
4935
+
4936
+ options.toShow[ 0 ].style[ settings.prop ] =
4937
+ ( percentDone * showProps[ settings.prop ].value )
4938
+ + showProps[ settings.prop ].unit;
4939
+ },
4940
+ duration: options.duration,
4941
+ easing: options.easing,
4942
+ complete: function() {
4943
+ if ( !options.autoHeight ) {
4944
+ options.toShow.css( "height", "" );
4945
+ }
4946
+ options.toShow.css({
4947
+ width: originalWidth,
4948
+ overflow: overflow
4949
+ });
4950
+ options.complete();
4951
+ }
4952
+ });
4953
+ },
4954
+ bounceslide: function( options ) {
4955
+ this.slide( options, {
4956
+ easing: options.down ? "easeOutBounce" : "swing",
4957
+ duration: options.down ? 1000 : 200
4958
+ });
4959
+ }
4960
+ }
4961
+ });
4962
+
4963
+ })( jQuery );
4964
+ /*
4965
+ * jQuery UI Autocomplete 1.8.17
4966
+ *
4967
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
4968
+ * Dual licensed under the MIT or GPL Version 2 licenses.
4969
+ * http://jquery.org/license
4970
+ *
4971
+ * http://docs.jquery.com/UI/Autocomplete
4972
+ *
4973
+ * Depends:
4974
+ * jquery.ui.core.js
4975
+ * jquery.ui.widget.js
4976
+ * jquery.ui.position.js
4977
+ */
4978
+ (function( $, undefined ) {
4979
+
4980
+ // used to prevent race conditions with remote data sources
4981
+ var requestIndex = 0;
4982
+
4983
+ $.widget( "ui.autocomplete", {
4984
+ options: {
4985
+ appendTo: "body",
4986
+ autoFocus: false,
4987
+ delay: 300,
4988
+ minLength: 1,
4989
+ position: {
4990
+ my: "left top",
4991
+ at: "left bottom",
4992
+ collision: "none"
4993
+ },
4994
+ source: null
4995
+ },
4996
+
4997
+ pending: 0,
4998
+
4999
+ _create: function() {
5000
+ var self = this,
5001
+ doc = this.element[ 0 ].ownerDocument,
5002
+ suppressKeyPress;
5003
+
5004
+ this.element
5005
+ .addClass( "ui-autocomplete-input" )
5006
+ .attr( "autocomplete", "off" )
5007
+ // TODO verify these actually work as intended
5008
+ .attr({
5009
+ role: "textbox",
5010
+ "aria-autocomplete": "list",
5011
+ "aria-haspopup": "true"
5012
+ })
5013
+ .bind( "keydown.autocomplete", function( event ) {
5014
+ if ( self.options.disabled || self.element.propAttr( "readOnly" ) ) {
5015
+ return;
5016
+ }
5017
+
5018
+ suppressKeyPress = false;
5019
+ var keyCode = $.ui.keyCode;
5020
+ switch( event.keyCode ) {
5021
+ case keyCode.PAGE_UP:
5022
+ self._move( "previousPage", event );
5023
+ break;
5024
+ case keyCode.PAGE_DOWN:
5025
+ self._move( "nextPage", event );
5026
+ break;
5027
+ case keyCode.UP:
5028
+ self._move( "previous", event );
5029
+ // prevent moving cursor to beginning of text field in some browsers
5030
+ event.preventDefault();
5031
+ break;
5032
+ case keyCode.DOWN:
5033
+ self._move( "next", event );
5034
+ // prevent moving cursor to end of text field in some browsers
5035
+ event.preventDefault();
5036
+ break;
5037
+ case keyCode.ENTER:
5038
+ case keyCode.NUMPAD_ENTER:
5039
+ // when menu is open and has focus
5040
+ if ( self.menu.active ) {
5041
+ // #6055 - Opera still allows the keypress to occur
5042
+ // which causes forms to submit
5043
+ suppressKeyPress = true;
5044
+ event.preventDefault();
5045
+ }
5046
+ //passthrough - ENTER and TAB both select the current element
5047
+ case keyCode.TAB:
5048
+ if ( !self.menu.active ) {
5049
+ return;
5050
+ }
5051
+ self.menu.select( event );
5052
+ break;
5053
+ case keyCode.ESCAPE:
5054
+ self.element.val( self.term );
5055
+ self.close( event );
5056
+ break;
5057
+ default:
5058
+ // keypress is triggered before the input value is changed
5059
+ clearTimeout( self.searching );
5060
+ self.searching = setTimeout(function() {
5061
+ // only search if the value has changed
5062
+ if ( self.term != self.element.val() ) {
5063
+ self.selectedItem = null;
5064
+ self.search( null, event );
5065
+ }
5066
+ }, self.options.delay );
5067
+ break;
5068
+ }
5069
+ })
5070
+ .bind( "keypress.autocomplete", function( event ) {
5071
+ if ( suppressKeyPress ) {
5072
+ suppressKeyPress = false;
5073
+ event.preventDefault();
5074
+ }
5075
+ })
5076
+ .bind( "focus.autocomplete", function() {
5077
+ if ( self.options.disabled ) {
5078
+ return;
5079
+ }
5080
+
5081
+ self.selectedItem = null;
5082
+ self.previous = self.element.val();
5083
+ })
5084
+ .bind( "blur.autocomplete", function( event ) {
5085
+ if ( self.options.disabled ) {
5086
+ return;
5087
+ }
5088
+
5089
+ clearTimeout( self.searching );
5090
+ // clicks on the menu (or a button to trigger a search) will cause a blur event
5091
+ self.closing = setTimeout(function() {
5092
+ self.close( event );
5093
+ self._change( event );
5094
+ }, 150 );
5095
+ });
5096
+ this._initSource();
5097
+ this.response = function() {
5098
+ return self._response.apply( self, arguments );
5099
+ };
5100
+ this.menu = $( "<ul></ul>" )
5101
+ .addClass( "ui-autocomplete" )
5102
+ .appendTo( $( this.options.appendTo || "body", doc )[0] )
5103
+ // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
5104
+ .mousedown(function( event ) {
5105
+ // clicking on the scrollbar causes focus to shift to the body
5106
+ // but we can't detect a mouseup or a click immediately afterward
5107
+ // so we have to track the next mousedown and close the menu if
5108
+ // the user clicks somewhere outside of the autocomplete
5109
+ var menuElement = self.menu.element[ 0 ];
5110
+ if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
5111
+ setTimeout(function() {
5112
+ $( document ).one( 'mousedown', function( event ) {
5113
+ if ( event.target !== self.element[ 0 ] &&
5114
+ event.target !== menuElement &&
5115
+ !$.ui.contains( menuElement, event.target ) ) {
5116
+ self.close();
5117
+ }
5118
+ });
5119
+ }, 1 );
5120
+ }
5121
+
5122
+ // use another timeout to make sure the blur-event-handler on the input was already triggered
5123
+ setTimeout(function() {
5124
+ clearTimeout( self.closing );
5125
+ }, 13);
5126
+ })
5127
+ .menu({
5128
+ focus: function( event, ui ) {
5129
+ var item = ui.item.data( "item.autocomplete" );
5130
+ if ( false !== self._trigger( "focus", event, { item: item } ) ) {
5131
+ // use value to match what will end up in the input, if it was a key event
5132
+ if ( /^key/.test(event.originalEvent.type) ) {
5133
+ self.element.val( item.value );
5134
+ }
5135
+ }
5136
+ },
5137
+ selected: function( event, ui ) {
5138
+ var item = ui.item.data( "item.autocomplete" ),
5139
+ previous = self.previous;
5140
+
5141
+ // only trigger when focus was lost (click on menu)
5142
+ if ( self.element[0] !== doc.activeElement ) {
5143
+ self.element.focus();
5144
+ self.previous = previous;
5145
+ // #6109 - IE triggers two focus events and the second
5146
+ // is asynchronous, so we need to reset the previous
5147
+ // term synchronously and asynchronously :-(
5148
+ setTimeout(function() {
5149
+ self.previous = previous;
5150
+ self.selectedItem = item;
5151
+ }, 1);
5152
+ }
5153
+
5154
+ if ( false !== self._trigger( "select", event, { item: item } ) ) {
5155
+ self.element.val( item.value );
5156
+ }
5157
+ // reset the term after the select event
5158
+ // this allows custom select handling to work properly
5159
+ self.term = self.element.val();
5160
+
5161
+ self.close( event );
5162
+ self.selectedItem = item;
5163
+ },
5164
+ blur: function( event, ui ) {
5165
+ // don't set the value of the text field if it's already correct
5166
+ // this prevents moving the cursor unnecessarily
5167
+ if ( self.menu.element.is(":visible") &&
5168
+ ( self.element.val() !== self.term ) ) {
5169
+ self.element.val( self.term );
5170
+ }
5171
+ }
5172
+ })
5173
+ .zIndex( this.element.zIndex() + 1 )
5174
+ // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
5175
+ .css({ top: 0, left: 0 })
5176
+ .hide()
5177
+ .data( "menu" );
5178
+ if ( $.fn.bgiframe ) {
5179
+ this.menu.element.bgiframe();
5180
+ }
5181
+ // turning off autocomplete prevents the browser from remembering the
5182
+ // value when navigating through history, so we re-enable autocomplete
5183
+ // if the page is unloaded before the widget is destroyed. #7790
5184
+ self.beforeunloadHandler = function() {
5185
+ self.element.removeAttr( "autocomplete" );
5186
+ };
5187
+ $( window ).bind( "beforeunload", self.beforeunloadHandler );
5188
+ },
5189
+
5190
+ destroy: function() {
5191
+ this.element
5192
+ .removeClass( "ui-autocomplete-input" )
5193
+ .removeAttr( "autocomplete" )
5194
+ .removeAttr( "role" )
5195
+ .removeAttr( "aria-autocomplete" )
5196
+ .removeAttr( "aria-haspopup" );
5197
+ this.menu.element.remove();
5198
+ $( window ).unbind( "beforeunload", this.beforeunloadHandler );
5199
+ $.Widget.prototype.destroy.call( this );
5200
+ },
5201
+
5202
+ _setOption: function( key, value ) {
5203
+ $.Widget.prototype._setOption.apply( this, arguments );
5204
+ if ( key === "source" ) {
5205
+ this._initSource();
5206
+ }
5207
+ if ( key === "appendTo" ) {
5208
+ this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
5209
+ }
5210
+ if ( key === "disabled" && value && this.xhr ) {
5211
+ this.xhr.abort();
5212
+ }
5213
+ },
5214
+
5215
+ _initSource: function() {
5216
+ var self = this,
5217
+ array,
5218
+ url;
5219
+ if ( $.isArray(this.options.source) ) {
5220
+ array = this.options.source;
5221
+ this.source = function( request, response ) {
5222
+ response( $.ui.autocomplete.filter(array, request.term) );
5223
+ };
5224
+ } else if ( typeof this.options.source === "string" ) {
5225
+ url = this.options.source;
5226
+ this.source = function( request, response ) {
5227
+ if ( self.xhr ) {
5228
+ self.xhr.abort();
5229
+ }
5230
+ self.xhr = $.ajax({
5231
+ url: url,
5232
+ data: request,
5233
+ dataType: "json",
5234
+ autocompleteRequest: ++requestIndex,
5235
+ success: function( data, status ) {
5236
+ if ( this.autocompleteRequest === requestIndex ) {
5237
+ response( data );
5238
+ }
5239
+ },
5240
+ error: function() {
5241
+ if ( this.autocompleteRequest === requestIndex ) {
5242
+ response( [] );
5243
+ }
5244
+ }
5245
+ });
5246
+ };
5247
+ } else {
5248
+ this.source = this.options.source;
5249
+ }
5250
+ },
5251
+
5252
+ search: function( value, event ) {
5253
+ value = value != null ? value : this.element.val();
5254
+
5255
+ // always save the actual value, not the one passed as an argument
5256
+ this.term = this.element.val();
5257
+
5258
+ if ( value.length < this.options.minLength ) {
5259
+ return this.close( event );
5260
+ }
5261
+
5262
+ clearTimeout( this.closing );
5263
+ if ( this._trigger( "search", event ) === false ) {
5264
+ return;
5265
+ }
5266
+
5267
+ return this._search( value );
5268
+ },
5269
+
5270
+ _search: function( value ) {
5271
+ this.pending++;
5272
+ this.element.addClass( "ui-autocomplete-loading" );
5273
+
5274
+ this.source( { term: value }, this.response );
5275
+ },
5276
+
5277
+ _response: function( content ) {
5278
+ if ( !this.options.disabled && content && content.length ) {
5279
+ content = this._normalize( content );
5280
+ this._suggest( content );
5281
+ this._trigger( "open" );
5282
+ } else {
5283
+ this.close();
5284
+ }
5285
+ this.pending--;
5286
+ if ( !this.pending ) {
5287
+ this.element.removeClass( "ui-autocomplete-loading" );
5288
+ }
5289
+ },
5290
+
5291
+ close: function( event ) {
5292
+ clearTimeout( this.closing );
5293
+ if ( this.menu.element.is(":visible") ) {
5294
+ this.menu.element.hide();
5295
+ this.menu.deactivate();
5296
+ this._trigger( "close", event );
5297
+ }
5298
+ },
5299
+
5300
+ _change: function( event ) {
5301
+ if ( this.previous !== this.element.val() ) {
5302
+ this._trigger( "change", event, { item: this.selectedItem } );
5303
+ }
5304
+ },
5305
+
5306
+ _normalize: function( items ) {
5307
+ // assume all items have the right format when the first item is complete
5308
+ if ( items.length && items[0].label && items[0].value ) {
5309
+ return items;
5310
+ }
5311
+ return $.map( items, function(item) {
5312
+ if ( typeof item === "string" ) {
5313
+ return {
5314
+ label: item,
5315
+ value: item
5316
+ };
5317
+ }
5318
+ return $.extend({
5319
+ label: item.label || item.value,
5320
+ value: item.value || item.label
5321
+ }, item );
5322
+ });
5323
+ },
5324
+
5325
+ _suggest: function( items ) {
5326
+ var ul = this.menu.element
5327
+ .empty()
5328
+ .zIndex( this.element.zIndex() + 1 );
5329
+ this._renderMenu( ul, items );
5330
+ // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
5331
+ this.menu.deactivate();
5332
+ this.menu.refresh();
5333
+
5334
+ // size and position menu
5335
+ ul.show();
5336
+ this._resizeMenu();
5337
+ ul.position( $.extend({
5338
+ of: this.element
5339
+ }, this.options.position ));
5340
+
5341
+ if ( this.options.autoFocus ) {
5342
+ this.menu.next( new $.Event("mouseover") );
5343
+ }
5344
+ },
5345
+
5346
+ _resizeMenu: function() {
5347
+ var ul = this.menu.element;
5348
+ ul.outerWidth( Math.max(
5349
+ // Firefox wraps long text (possibly a rounding bug)
5350
+ // so we add 1px to avoid the wrapping (#7513)
5351
+ ul.width( "" ).outerWidth() + 1,
5352
+ this.element.outerWidth()
5353
+ ) );
5354
+ },
5355
+
5356
+ _renderMenu: function( ul, items ) {
5357
+ var self = this;
5358
+ $.each( items, function( index, item ) {
5359
+ self._renderItem( ul, item );
5360
+ });
5361
+ },
5362
+
5363
+ _renderItem: function( ul, item) {
5364
+ return $( "<li></li>" )
5365
+ .data( "item.autocomplete", item )
5366
+ .append( $( "<a></a>" ).text( item.label ) )
5367
+ .appendTo( ul );
5368
+ },
5369
+
5370
+ _move: function( direction, event ) {
5371
+ if ( !this.menu.element.is(":visible") ) {
5372
+ this.search( null, event );
5373
+ return;
5374
+ }
5375
+ if ( this.menu.first() && /^previous/.test(direction) ||
5376
+ this.menu.last() && /^next/.test(direction) ) {
5377
+ this.element.val( this.term );
5378
+ this.menu.deactivate();
5379
+ return;
5380
+ }
5381
+ this.menu[ direction ]( event );
5382
+ },
5383
+
5384
+ widget: function() {
5385
+ return this.menu.element;
5386
+ }
5387
+ });
5388
+
5389
+ $.extend( $.ui.autocomplete, {
5390
+ escapeRegex: function( value ) {
5391
+ return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
5392
+ },
5393
+ filter: function(array, term) {
5394
+ var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
5395
+ return $.grep( array, function(value) {
5396
+ return matcher.test( value.label || value.value || value );
5397
+ });
5398
+ }
5399
+ });
5400
+
5401
+ }( jQuery ));
5402
+
5403
+ /*
5404
+ * jQuery UI Menu (not officially released)
5405
+ *
5406
+ * This widget isn't yet finished and the API is subject to change. We plan to finish
5407
+ * it for the next release. You're welcome to give it a try anyway and give us feedback,
5408
+ * as long as you're okay with migrating your code later on. We can help with that, too.
5409
+ *
5410
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
5411
+ * Dual licensed under the MIT or GPL Version 2 licenses.
5412
+ * http://jquery.org/license
5413
+ *
5414
+ * http://docs.jquery.com/UI/Menu
5415
+ *
5416
+ * Depends:
5417
+ * jquery.ui.core.js
5418
+ * jquery.ui.widget.js
5419
+ */
5420
+ (function($) {
5421
+
5422
+ $.widget("ui.menu", {
5423
+ _create: function() {
5424
+ var self = this;
5425
+ this.element
5426
+ .addClass("ui-menu ui-widget ui-widget-content ui-corner-all")
5427
+ .attr({
5428
+ role: "listbox",
5429
+ "aria-activedescendant": "ui-active-menuitem"
5430
+ })
5431
+ .click(function( event ) {
5432
+ if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {
5433
+ return;
5434
+ }
5435
+ // temporary
5436
+ event.preventDefault();
5437
+ self.select( event );
5438
+ });
5439
+ this.refresh();
5440
+ },
5441
+
5442
+ refresh: function() {
5443
+ var self = this;
5444
+
5445
+ // don't refresh list items that are already adapted
5446
+ var items = this.element.children("li:not(.ui-menu-item):has(a)")
5447
+ .addClass("ui-menu-item")
5448
+ .attr("role", "menuitem");
5449
+
5450
+ items.children("a")
5451
+ .addClass("ui-corner-all")
5452
+ .attr("tabindex", -1)
5453
+ // mouseenter doesn't work with event delegation
5454
+ .mouseenter(function( event ) {
5455
+ self.activate( event, $(this).parent() );
5456
+ })
5457
+ .mouseleave(function() {
5458
+ self.deactivate();
5459
+ });
5460
+ },
5461
+
5462
+ activate: function( event, item ) {
5463
+ this.deactivate();
5464
+ if (this.hasScroll()) {
5465
+ var offset = item.offset().top - this.element.offset().top,
5466
+ scroll = this.element.scrollTop(),
5467
+ elementHeight = this.element.height();
5468
+ if (offset < 0) {
5469
+ this.element.scrollTop( scroll + offset);
5470
+ } else if (offset >= elementHeight) {
5471
+ this.element.scrollTop( scroll + offset - elementHeight + item.height());
5472
+ }
5473
+ }
5474
+ this.active = item.eq(0)
5475
+ .children("a")
5476
+ .addClass("ui-state-hover")
5477
+ .attr("id", "ui-active-menuitem")
5478
+ .end();
5479
+ this._trigger("focus", event, { item: item });
5480
+ },
5481
+
5482
+ deactivate: function() {
5483
+ if (!this.active) { return; }
5484
+
5485
+ this.active.children("a")
5486
+ .removeClass("ui-state-hover")
5487
+ .removeAttr("id");
5488
+ this._trigger("blur");
5489
+ this.active = null;
5490
+ },
5491
+
5492
+ next: function(event) {
5493
+ this.move("next", ".ui-menu-item:first", event);
5494
+ },
5495
+
5496
+ previous: function(event) {
5497
+ this.move("prev", ".ui-menu-item:last", event);
5498
+ },
5499
+
5500
+ first: function() {
5501
+ return this.active && !this.active.prevAll(".ui-menu-item").length;
5502
+ },
5503
+
5504
+ last: function() {
5505
+ return this.active && !this.active.nextAll(".ui-menu-item").length;
5506
+ },
5507
+
5508
+ move: function(direction, edge, event) {
5509
+ if (!this.active) {
5510
+ this.activate(event, this.element.children(edge));
5511
+ return;
5512
+ }
5513
+ var next = this.active[direction + "All"](".ui-menu-item").eq(0);
5514
+ if (next.length) {
5515
+ this.activate(event, next);
5516
+ } else {
5517
+ this.activate(event, this.element.children(edge));
5518
+ }
5519
+ },
5520
+
5521
+ // TODO merge with previousPage
5522
+ nextPage: function(event) {
5523
+ if (this.hasScroll()) {
5524
+ // TODO merge with no-scroll-else
5525
+ if (!this.active || this.last()) {
5526
+ this.activate(event, this.element.children(".ui-menu-item:first"));
5527
+ return;
5528
+ }
5529
+ var base = this.active.offset().top,
5530
+ height = this.element.height(),
5531
+ result = this.element.children(".ui-menu-item").filter(function() {
5532
+ var close = $(this).offset().top - base - height + $(this).height();
5533
+ // TODO improve approximation
5534
+ return close < 10 && close > -10;
5535
+ });
5536
+
5537
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
5538
+ if (!result.length) {
5539
+ result = this.element.children(".ui-menu-item:last");
5540
+ }
5541
+ this.activate(event, result);
5542
+ } else {
5543
+ this.activate(event, this.element.children(".ui-menu-item")
5544
+ .filter(!this.active || this.last() ? ":first" : ":last"));
5545
+ }
5546
+ },
5547
+
5548
+ // TODO merge with nextPage
5549
+ previousPage: function(event) {
5550
+ if (this.hasScroll()) {
5551
+ // TODO merge with no-scroll-else
5552
+ if (!this.active || this.first()) {
5553
+ this.activate(event, this.element.children(".ui-menu-item:last"));
5554
+ return;
5555
+ }
5556
+
5557
+ var base = this.active.offset().top,
5558
+ height = this.element.height();
5559
+ result = this.element.children(".ui-menu-item").filter(function() {
5560
+ var close = $(this).offset().top - base + height - $(this).height();
5561
+ // TODO improve approximation
5562
+ return close < 10 && close > -10;
5563
+ });
5564
+
5565
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
5566
+ if (!result.length) {
5567
+ result = this.element.children(".ui-menu-item:first");
5568
+ }
5569
+ this.activate(event, result);
5570
+ } else {
5571
+ this.activate(event, this.element.children(".ui-menu-item")
5572
+ .filter(!this.active || this.first() ? ":last" : ":first"));
5573
+ }
5574
+ },
5575
+
5576
+ hasScroll: function() {
5577
+ return this.element.height() < this.element[ $.fn.prop ? "prop" : "attr" ]("scrollHeight");
5578
+ },
5579
+
5580
+ select: function( event ) {
5581
+ this._trigger("selected", event, { item: this.active });
5582
+ }
5583
+ });
5584
+
5585
+ }(jQuery));
5586
+ /*
5587
+ * jQuery UI Button 1.8.17
5588
+ *
5589
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5590
+ * Dual licensed under the MIT or GPL Version 2 licenses.
5591
+ * http://jquery.org/license
5592
+ *
5593
+ * http://docs.jquery.com/UI/Button
5594
+ *
5595
+ * Depends:
5596
+ * jquery.ui.core.js
5597
+ * jquery.ui.widget.js
5598
+ */
5599
+ (function( $, undefined ) {
5600
+
5601
+ var lastActive, startXPos, startYPos, clickDragged,
5602
+ baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
5603
+ stateClasses = "ui-state-hover ui-state-active ",
5604
+ typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
5605
+ formResetHandler = function() {
5606
+ var buttons = $( this ).find( ":ui-button" );
5607
+ setTimeout(function() {
5608
+ buttons.button( "refresh" );
5609
+ }, 1 );
5610
+ },
5611
+ radioGroup = function( radio ) {
5612
+ var name = radio.name,
5613
+ form = radio.form,
5614
+ radios = $( [] );
5615
+ if ( name ) {
5616
+ if ( form ) {
5617
+ radios = $( form ).find( "[name='" + name + "']" );
5618
+ } else {
5619
+ radios = $( "[name='" + name + "']", radio.ownerDocument )
5620
+ .filter(function() {
5621
+ return !this.form;
5622
+ });
5623
+ }
5624
+ }
5625
+ return radios;
5626
+ };
5627
+
5628
+ $.widget( "ui.button", {
5629
+ options: {
5630
+ disabled: null,
5631
+ text: true,
5632
+ label: null,
5633
+ icons: {
5634
+ primary: null,
5635
+ secondary: null
5636
+ }
5637
+ },
5638
+ _create: function() {
5639
+ this.element.closest( "form" )
5640
+ .unbind( "reset.button" )
5641
+ .bind( "reset.button", formResetHandler );
5642
+
5643
+ if ( typeof this.options.disabled !== "boolean" ) {
5644
+ this.options.disabled = this.element.propAttr( "disabled" );
5645
+ }
5646
+
5647
+ this._determineButtonType();
5648
+ this.hasTitle = !!this.buttonElement.attr( "title" );
5649
+
5650
+ var self = this,
5651
+ options = this.options,
5652
+ toggleButton = this.type === "checkbox" || this.type === "radio",
5653
+ hoverClass = "ui-state-hover" + ( !toggleButton ? " ui-state-active" : "" ),
5654
+ focusClass = "ui-state-focus";
5655
+
5656
+ if ( options.label === null ) {
5657
+ options.label = this.buttonElement.html();
5658
+ }
5659
+
5660
+ if ( this.element.is( ":disabled" ) ) {
5661
+ options.disabled = true;
5662
+ }
5663
+
5664
+ this.buttonElement
5665
+ .addClass( baseClasses )
5666
+ .attr( "role", "button" )
5667
+ .bind( "mouseenter.button", function() {
5668
+ if ( options.disabled ) {
5669
+ return;
5670
+ }
5671
+ $( this ).addClass( "ui-state-hover" );
5672
+ if ( this === lastActive ) {
5673
+ $( this ).addClass( "ui-state-active" );
5674
+ }
5675
+ })
5676
+ .bind( "mouseleave.button", function() {
5677
+ if ( options.disabled ) {
5678
+ return;
5679
+ }
5680
+ $( this ).removeClass( hoverClass );
5681
+ })
5682
+ .bind( "click.button", function( event ) {
5683
+ if ( options.disabled ) {
5684
+ event.preventDefault();
5685
+ event.stopImmediatePropagation();
5686
+ }
5687
+ });
5688
+
5689
+ this.element
5690
+ .bind( "focus.button", function() {
5691
+ // no need to check disabled, focus won't be triggered anyway
5692
+ self.buttonElement.addClass( focusClass );
5693
+ })
5694
+ .bind( "blur.button", function() {
5695
+ self.buttonElement.removeClass( focusClass );
5696
+ });
5697
+
5698
+ if ( toggleButton ) {
5699
+ this.element.bind( "change.button", function() {
5700
+ if ( clickDragged ) {
5701
+ return;
5702
+ }
5703
+ self.refresh();
5704
+ });
5705
+ // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
5706
+ // prevents issue where button state changes but checkbox/radio checked state
5707
+ // does not in Firefox (see ticket #6970)
5708
+ this.buttonElement
5709
+ .bind( "mousedown.button", function( event ) {
5710
+ if ( options.disabled ) {
5711
+ return;
5712
+ }
5713
+ clickDragged = false;
5714
+ startXPos = event.pageX;
5715
+ startYPos = event.pageY;
5716
+ })
5717
+ .bind( "mouseup.button", function( event ) {
5718
+ if ( options.disabled ) {
5719
+ return;
5720
+ }
5721
+ if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
5722
+ clickDragged = true;
5723
+ }
5724
+ });
5725
+ }
5726
+
5727
+ if ( this.type === "checkbox" ) {
5728
+ this.buttonElement.bind( "click.button", function() {
5729
+ if ( options.disabled || clickDragged ) {
5730
+ return false;
5731
+ }
5732
+ $( this ).toggleClass( "ui-state-active" );
5733
+ self.buttonElement.attr( "aria-pressed", self.element[0].checked );
5734
+ });
5735
+ } else if ( this.type === "radio" ) {
5736
+ this.buttonElement.bind( "click.button", function() {
5737
+ if ( options.disabled || clickDragged ) {
5738
+ return false;
5739
+ }
5740
+ $( this ).addClass( "ui-state-active" );
5741
+ self.buttonElement.attr( "aria-pressed", "true" );
5742
+
5743
+ var radio = self.element[ 0 ];
5744
+ radioGroup( radio )
5745
+ .not( radio )
5746
+ .map(function() {
5747
+ return $( this ).button( "widget" )[ 0 ];
5748
+ })
5749
+ .removeClass( "ui-state-active" )
5750
+ .attr( "aria-pressed", "false" );
5751
+ });
5752
+ } else {
5753
+ this.buttonElement
5754
+ .bind( "mousedown.button", function() {
5755
+ if ( options.disabled ) {
5756
+ return false;
5757
+ }
5758
+ $( this ).addClass( "ui-state-active" );
5759
+ lastActive = this;
5760
+ $( document ).one( "mouseup", function() {
5761
+ lastActive = null;
5762
+ });
5763
+ })
5764
+ .bind( "mouseup.button", function() {
5765
+ if ( options.disabled ) {
5766
+ return false;
5767
+ }
5768
+ $( this ).removeClass( "ui-state-active" );
5769
+ })
5770
+ .bind( "keydown.button", function(event) {
5771
+ if ( options.disabled ) {
5772
+ return false;
5773
+ }
5774
+ if ( event.keyCode == $.ui.keyCode.SPACE || event.keyCode == $.ui.keyCode.ENTER ) {
5775
+ $( this ).addClass( "ui-state-active" );
5776
+ }
5777
+ })
5778
+ .bind( "keyup.button", function() {
5779
+ $( this ).removeClass( "ui-state-active" );
5780
+ });
5781
+
5782
+ if ( this.buttonElement.is("a") ) {
5783
+ this.buttonElement.keyup(function(event) {
5784
+ if ( event.keyCode === $.ui.keyCode.SPACE ) {
5785
+ // TODO pass through original event correctly (just as 2nd argument doesn't work)
5786
+ $( this ).click();
5787
+ }
5788
+ });
5789
+ }
5790
+ }
5791
+
5792
+ // TODO: pull out $.Widget's handling for the disabled option into
5793
+ // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
5794
+ // be overridden by individual plugins
5795
+ this._setOption( "disabled", options.disabled );
5796
+ this._resetButton();
5797
+ },
5798
+
5799
+ _determineButtonType: function() {
5800
+
5801
+ if ( this.element.is(":checkbox") ) {
5802
+ this.type = "checkbox";
5803
+ } else if ( this.element.is(":radio") ) {
5804
+ this.type = "radio";
5805
+ } else if ( this.element.is("input") ) {
5806
+ this.type = "input";
5807
+ } else {
5808
+ this.type = "button";
5809
+ }
5810
+
5811
+ if ( this.type === "checkbox" || this.type === "radio" ) {
5812
+ // we don't search against the document in case the element
5813
+ // is disconnected from the DOM
5814
+ var ancestor = this.element.parents().filter(":last"),
5815
+ labelSelector = "label[for='" + this.element.attr("id") + "']";
5816
+ this.buttonElement = ancestor.find( labelSelector );
5817
+ if ( !this.buttonElement.length ) {
5818
+ ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
5819
+ this.buttonElement = ancestor.filter( labelSelector );
5820
+ if ( !this.buttonElement.length ) {
5821
+ this.buttonElement = ancestor.find( labelSelector );
5822
+ }
5823
+ }
5824
+ this.element.addClass( "ui-helper-hidden-accessible" );
5825
+
5826
+ var checked = this.element.is( ":checked" );
5827
+ if ( checked ) {
5828
+ this.buttonElement.addClass( "ui-state-active" );
5829
+ }
5830
+ this.buttonElement.attr( "aria-pressed", checked );
5831
+ } else {
5832
+ this.buttonElement = this.element;
5833
+ }
5834
+ },
5835
+
5836
+ widget: function() {
5837
+ return this.buttonElement;
5838
+ },
5839
+
5840
+ destroy: function() {
5841
+ this.element
5842
+ .removeClass( "ui-helper-hidden-accessible" );
5843
+ this.buttonElement
5844
+ .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
5845
+ .removeAttr( "role" )
5846
+ .removeAttr( "aria-pressed" )
5847
+ .html( this.buttonElement.find(".ui-button-text").html() );
5848
+
5849
+ if ( !this.hasTitle ) {
5850
+ this.buttonElement.removeAttr( "title" );
5851
+ }
5852
+
5853
+ $.Widget.prototype.destroy.call( this );
5854
+ },
5855
+
5856
+ _setOption: function( key, value ) {
5857
+ $.Widget.prototype._setOption.apply( this, arguments );
5858
+ if ( key === "disabled" ) {
5859
+ if ( value ) {
5860
+ this.element.propAttr( "disabled", true );
5861
+ } else {
5862
+ this.element.propAttr( "disabled", false );
5863
+ }
5864
+ return;
5865
+ }
5866
+ this._resetButton();
5867
+ },
5868
+
5869
+ refresh: function() {
5870
+ var isDisabled = this.element.is( ":disabled" );
5871
+ if ( isDisabled !== this.options.disabled ) {
5872
+ this._setOption( "disabled", isDisabled );
5873
+ }
5874
+ if ( this.type === "radio" ) {
5875
+ radioGroup( this.element[0] ).each(function() {
5876
+ if ( $( this ).is( ":checked" ) ) {
5877
+ $( this ).button( "widget" )
5878
+ .addClass( "ui-state-active" )
5879
+ .attr( "aria-pressed", "true" );
5880
+ } else {
5881
+ $( this ).button( "widget" )
5882
+ .removeClass( "ui-state-active" )
5883
+ .attr( "aria-pressed", "false" );
5884
+ }
5885
+ });
5886
+ } else if ( this.type === "checkbox" ) {
5887
+ if ( this.element.is( ":checked" ) ) {
5888
+ this.buttonElement
5889
+ .addClass( "ui-state-active" )
5890
+ .attr( "aria-pressed", "true" );
5891
+ } else {
5892
+ this.buttonElement
5893
+ .removeClass( "ui-state-active" )
5894
+ .attr( "aria-pressed", "false" );
5895
+ }
5896
+ }
5897
+ },
5898
+
5899
+ _resetButton: function() {
5900
+ if ( this.type === "input" ) {
5901
+ if ( this.options.label ) {
5902
+ this.element.val( this.options.label );
5903
+ }
5904
+ return;
5905
+ }
5906
+ var buttonElement = this.buttonElement.removeClass( typeClasses ),
5907
+ buttonText = $( "<span></span>", this.element[0].ownerDocument )
5908
+ .addClass( "ui-button-text" )
5909
+ .html( this.options.label )
5910
+ .appendTo( buttonElement.empty() )
5911
+ .text(),
5912
+ icons = this.options.icons,
5913
+ multipleIcons = icons.primary && icons.secondary,
5914
+ buttonClasses = [];
5915
+
5916
+ if ( icons.primary || icons.secondary ) {
5917
+ if ( this.options.text ) {
5918
+ buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
5919
+ }
5920
+
5921
+ if ( icons.primary ) {
5922
+ buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
5923
+ }
5924
+
5925
+ if ( icons.secondary ) {
5926
+ buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
5927
+ }
5928
+
5929
+ if ( !this.options.text ) {
5930
+ buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
5931
+
5932
+ if ( !this.hasTitle ) {
5933
+ buttonElement.attr( "title", buttonText );
5934
+ }
5935
+ }
5936
+ } else {
5937
+ buttonClasses.push( "ui-button-text-only" );
5938
+ }
5939
+ buttonElement.addClass( buttonClasses.join( " " ) );
5940
+ }
5941
+ });
5942
+
5943
+ $.widget( "ui.buttonset", {
5944
+ options: {
5945
+ items: ":button, :submit, :reset, :checkbox, :radio, a, :data(button)"
5946
+ },
5947
+
5948
+ _create: function() {
5949
+ this.element.addClass( "ui-buttonset" );
5950
+ },
5951
+
5952
+ _init: function() {
5953
+ this.refresh();
5954
+ },
5955
+
5956
+ _setOption: function( key, value ) {
5957
+ if ( key === "disabled" ) {
5958
+ this.buttons.button( "option", key, value );
5959
+ }
5960
+
5961
+ $.Widget.prototype._setOption.apply( this, arguments );
5962
+ },
5963
+
5964
+ refresh: function() {
5965
+ var rtl = this.element.css( "direction" ) === "rtl";
5966
+
5967
+ this.buttons = this.element.find( this.options.items )
5968
+ .filter( ":ui-button" )
5969
+ .button( "refresh" )
5970
+ .end()
5971
+ .not( ":ui-button" )
5972
+ .button()
5973
+ .end()
5974
+ .map(function() {
5975
+ return $( this ).button( "widget" )[ 0 ];
5976
+ })
5977
+ .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
5978
+ .filter( ":first" )
5979
+ .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
5980
+ .end()
5981
+ .filter( ":last" )
5982
+ .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
5983
+ .end()
5984
+ .end();
5985
+ },
5986
+
5987
+ destroy: function() {
5988
+ this.element.removeClass( "ui-buttonset" );
5989
+ this.buttons
5990
+ .map(function() {
5991
+ return $( this ).button( "widget" )[ 0 ];
5992
+ })
5993
+ .removeClass( "ui-corner-left ui-corner-right" )
5994
+ .end()
5995
+ .button( "destroy" );
5996
+
5997
+ $.Widget.prototype.destroy.call( this );
5998
+ }
5999
+ });
6000
+
6001
+ }( jQuery ) );
6002
+ /*
6003
+ * jQuery UI Dialog 1.8.17
6004
+ *
6005
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
6006
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6007
+ * http://jquery.org/license
6008
+ *
6009
+ * http://docs.jquery.com/UI/Dialog
6010
+ *
6011
+ * Depends:
6012
+ * jquery.ui.core.js
6013
+ * jquery.ui.widget.js
6014
+ * jquery.ui.button.js
6015
+ * jquery.ui.draggable.js
6016
+ * jquery.ui.mouse.js
6017
+ * jquery.ui.position.js
6018
+ * jquery.ui.resizable.js
6019
+ */
6020
+ (function( $, undefined ) {
6021
+
6022
+ var uiDialogClasses =
6023
+ 'ui-dialog ' +
6024
+ 'ui-widget ' +
6025
+ 'ui-widget-content ' +
6026
+ 'ui-corner-all ',
6027
+ sizeRelatedOptions = {
6028
+ buttons: true,
6029
+ height: true,
6030
+ maxHeight: true,
6031
+ maxWidth: true,
6032
+ minHeight: true,
6033
+ minWidth: true,
6034
+ width: true
6035
+ },
6036
+ resizableRelatedOptions = {
6037
+ maxHeight: true,
6038
+ maxWidth: true,
6039
+ minHeight: true,
6040
+ minWidth: true
6041
+ },
6042
+ // support for jQuery 1.3.2 - handle common attrFn methods for dialog
6043
+ attrFn = $.attrFn || {
6044
+ val: true,
6045
+ css: true,
6046
+ html: true,
6047
+ text: true,
6048
+ data: true,
6049
+ width: true,
6050
+ height: true,
6051
+ offset: true,
6052
+ click: true
6053
+ };
6054
+
6055
+ $.widget("ui.dialog", {
6056
+ options: {
6057
+ autoOpen: true,
6058
+ buttons: {},
6059
+ closeOnEscape: true,
6060
+ closeText: 'close',
6061
+ dialogClass: '',
6062
+ draggable: true,
6063
+ hide: null,
6064
+ height: 'auto',
6065
+ maxHeight: false,
6066
+ maxWidth: false,
6067
+ minHeight: 150,
6068
+ minWidth: 150,
6069
+ modal: false,
6070
+ position: {
6071
+ my: 'center',
6072
+ at: 'center',
6073
+ collision: 'fit',
6074
+ // ensure that the titlebar is never outside the document
6075
+ using: function(pos) {
6076
+ var topOffset = $(this).css(pos).offset().top;
6077
+ if (topOffset < 0) {
6078
+ $(this).css('top', pos.top - topOffset);
6079
+ }
6080
+ }
6081
+ },
6082
+ resizable: true,
6083
+ show: null,
6084
+ stack: true,
6085
+ title: '',
6086
+ width: 300,
6087
+ zIndex: 1000
6088
+ },
6089
+
6090
+ _create: function() {
6091
+ this.originalTitle = this.element.attr('title');
6092
+ // #5742 - .attr() might return a DOMElement
6093
+ if ( typeof this.originalTitle !== "string" ) {
6094
+ this.originalTitle = "";
6095
+ }
6096
+
6097
+ this.options.title = this.options.title || this.originalTitle;
6098
+ var self = this,
6099
+ options = self.options,
6100
+
6101
+ title = options.title || '&#160;',
6102
+ titleId = $.ui.dialog.getTitleId(self.element),
6103
+
6104
+ uiDialog = (self.uiDialog = $('<div></div>'))
6105
+ .appendTo(document.body)
6106
+ .hide()
6107
+ .addClass(uiDialogClasses + options.dialogClass)
6108
+ .css({
6109
+ zIndex: options.zIndex
6110
+ })
6111
+ // setting tabIndex makes the div focusable
6112
+ // setting outline to 0 prevents a border on focus in Mozilla
6113
+ .attr('tabIndex', -1).css('outline', 0).keydown(function(event) {
6114
+ if (options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
6115
+ event.keyCode === $.ui.keyCode.ESCAPE) {
6116
+
6117
+ self.close(event);
6118
+ event.preventDefault();
6119
+ }
6120
+ })
6121
+ .attr({
6122
+ role: 'dialog',
6123
+ 'aria-labelledby': titleId
6124
+ })
6125
+ .mousedown(function(event) {
6126
+ self.moveToTop(false, event);
6127
+ }),
6128
+
6129
+ uiDialogContent = self.element
6130
+ .show()
6131
+ .removeAttr('title')
6132
+ .addClass(
6133
+ 'ui-dialog-content ' +
6134
+ 'ui-widget-content')
6135
+ .appendTo(uiDialog),
6136
+
6137
+ uiDialogTitlebar = (self.uiDialogTitlebar = $('<div></div>'))
6138
+ .addClass(
6139
+ 'ui-dialog-titlebar ' +
6140
+ 'ui-widget-header ' +
6141
+ 'ui-corner-all ' +
6142
+ 'ui-helper-clearfix'
6143
+ )
6144
+ .prependTo(uiDialog),
6145
+
6146
+ uiDialogTitlebarClose = $('<a href="#"></a>')
6147
+ .addClass(
6148
+ 'ui-dialog-titlebar-close ' +
6149
+ 'ui-corner-all'
6150
+ )
6151
+ .attr('role', 'button')
6152
+ .hover(
6153
+ function() {
6154
+ uiDialogTitlebarClose.addClass('ui-state-hover');
6155
+ },
6156
+ function() {
6157
+ uiDialogTitlebarClose.removeClass('ui-state-hover');
6158
+ }
6159
+ )
6160
+ .focus(function() {
6161
+ uiDialogTitlebarClose.addClass('ui-state-focus');
6162
+ })
6163
+ .blur(function() {
6164
+ uiDialogTitlebarClose.removeClass('ui-state-focus');
6165
+ })
6166
+ .click(function(event) {
6167
+ self.close(event);
6168
+ return false;
6169
+ })
6170
+ .appendTo(uiDialogTitlebar),
6171
+
6172
+ uiDialogTitlebarCloseText = (self.uiDialogTitlebarCloseText = $('<span></span>'))
6173
+ .addClass(
6174
+ 'ui-icon ' +
6175
+ 'ui-icon-closethick'
6176
+ )
6177
+ .text(options.closeText)
6178
+ .appendTo(uiDialogTitlebarClose),
6179
+
6180
+ uiDialogTitle = $('<span></span>')
6181
+ .addClass('ui-dialog-title')
6182
+ .attr('id', titleId)
6183
+ .html(title)
6184
+ .prependTo(uiDialogTitlebar);
6185
+
6186
+ //handling of deprecated beforeclose (vs beforeClose) option
6187
+ //Ticket #4669 http://dev.jqueryui.com/ticket/4669
6188
+ //TODO: remove in 1.9pre
6189
+ if ($.isFunction(options.beforeclose) && !$.isFunction(options.beforeClose)) {
6190
+ options.beforeClose = options.beforeclose;
6191
+ }
6192
+
6193
+ uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection();
6194
+
6195
+ if (options.draggable && $.fn.draggable) {
6196
+ self._makeDraggable();
6197
+ }
6198
+ if (options.resizable && $.fn.resizable) {
6199
+ self._makeResizable();
6200
+ }
6201
+
6202
+ self._createButtons(options.buttons);
6203
+ self._isOpen = false;
6204
+
6205
+ if ($.fn.bgiframe) {
6206
+ uiDialog.bgiframe();
6207
+ }
6208
+ },
6209
+
6210
+ _init: function() {
6211
+ if ( this.options.autoOpen ) {
6212
+ this.open();
6213
+ }
6214
+ },
6215
+
6216
+ destroy: function() {
6217
+ var self = this;
6218
+
6219
+ if (self.overlay) {
6220
+ self.overlay.destroy();
6221
+ }
6222
+ self.uiDialog.hide();
6223
+ self.element
6224
+ .unbind('.dialog')
6225
+ .removeData('dialog')
6226
+ .removeClass('ui-dialog-content ui-widget-content')
6227
+ .hide().appendTo('body');
6228
+ self.uiDialog.remove();
6229
+
6230
+ if (self.originalTitle) {
6231
+ self.element.attr('title', self.originalTitle);
6232
+ }
6233
+
6234
+ return self;
6235
+ },
6236
+
6237
+ widget: function() {
6238
+ return this.uiDialog;
6239
+ },
6240
+
6241
+ close: function(event) {
6242
+ var self = this,
6243
+ maxZ, thisZ;
6244
+
6245
+ if (false === self._trigger('beforeClose', event)) {
6246
+ return;
6247
+ }
6248
+
6249
+ if (self.overlay) {
6250
+ self.overlay.destroy();
6251
+ }
6252
+ self.uiDialog.unbind('keypress.ui-dialog');
6253
+
6254
+ self._isOpen = false;
6255
+
6256
+ if (self.options.hide) {
6257
+ self.uiDialog.hide(self.options.hide, function() {
6258
+ self._trigger('close', event);
6259
+ });
6260
+ } else {
6261
+ self.uiDialog.hide();
6262
+ self._trigger('close', event);
6263
+ }
6264
+
6265
+ $.ui.dialog.overlay.resize();
6266
+
6267
+ // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
6268
+ if (self.options.modal) {
6269
+ maxZ = 0;
6270
+ $('.ui-dialog').each(function() {
6271
+ if (this !== self.uiDialog[0]) {
6272
+ thisZ = $(this).css('z-index');
6273
+ if(!isNaN(thisZ)) {
6274
+ maxZ = Math.max(maxZ, thisZ);
6275
+ }
6276
+ }
6277
+ });
6278
+ $.ui.dialog.maxZ = maxZ;
6279
+ }
6280
+
6281
+ return self;
6282
+ },
6283
+
6284
+ isOpen: function() {
6285
+ return this._isOpen;
6286
+ },
6287
+
6288
+ // the force parameter allows us to move modal dialogs to their correct
6289
+ // position on open
6290
+ moveToTop: function(force, event) {
6291
+ var self = this,
6292
+ options = self.options,
6293
+ saveScroll;
6294
+
6295
+ if ((options.modal && !force) ||
6296
+ (!options.stack && !options.modal)) {
6297
+ return self._trigger('focus', event);
6298
+ }
6299
+
6300
+ if (options.zIndex > $.ui.dialog.maxZ) {
6301
+ $.ui.dialog.maxZ = options.zIndex;
6302
+ }
6303
+ if (self.overlay) {
6304
+ $.ui.dialog.maxZ += 1;
6305
+ self.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ);
6306
+ }
6307
+
6308
+ //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
6309
+ // http://ui.jquery.com/bugs/ticket/3193
6310
+ saveScroll = { scrollTop: self.element.scrollTop(), scrollLeft: self.element.scrollLeft() };
6311
+ $.ui.dialog.maxZ += 1;
6312
+ self.uiDialog.css('z-index', $.ui.dialog.maxZ);
6313
+ self.element.attr(saveScroll);
6314
+ self._trigger('focus', event);
6315
+
6316
+ return self;
6317
+ },
6318
+
6319
+ open: function() {
6320
+ if (this._isOpen) { return; }
6321
+
6322
+ var self = this,
6323
+ options = self.options,
6324
+ uiDialog = self.uiDialog;
6325
+
6326
+ self.overlay = options.modal ? new $.ui.dialog.overlay(self) : null;
6327
+ self._size();
6328
+ self._position(options.position);
6329
+ uiDialog.show(options.show);
6330
+ self.moveToTop(true);
6331
+
6332
+ // prevent tabbing out of modal dialogs
6333
+ if ( options.modal ) {
6334
+ uiDialog.bind( "keydown.ui-dialog", function( event ) {
6335
+ if ( event.keyCode !== $.ui.keyCode.TAB ) {
6336
+ return;
6337
+ }
6338
+
6339
+ var tabbables = $(':tabbable', this),
6340
+ first = tabbables.filter(':first'),
6341
+ last = tabbables.filter(':last');
6342
+
6343
+ if (event.target === last[0] && !event.shiftKey) {
6344
+ first.focus(1);
6345
+ return false;
6346
+ } else if (event.target === first[0] && event.shiftKey) {
6347
+ last.focus(1);
6348
+ return false;
6349
+ }
6350
+ });
6351
+ }
6352
+
6353
+ // set focus to the first tabbable element in the content area or the first button
6354
+ // if there are no tabbable elements, set focus on the dialog itself
6355
+ $(self.element.find(':tabbable').get().concat(
6356
+ uiDialog.find('.ui-dialog-buttonpane :tabbable').get().concat(
6357
+ uiDialog.get()))).eq(0).focus();
6358
+
6359
+ self._isOpen = true;
6360
+ self._trigger('open');
6361
+
6362
+ return self;
6363
+ },
6364
+
6365
+ _createButtons: function(buttons) {
6366
+ var self = this,
6367
+ hasButtons = false,
6368
+ uiDialogButtonPane = $('<div></div>')
6369
+ .addClass(
6370
+ 'ui-dialog-buttonpane ' +
6371
+ 'ui-widget-content ' +
6372
+ 'ui-helper-clearfix'
6373
+ ),
6374
+ uiButtonSet = $( "<div></div>" )
6375
+ .addClass( "ui-dialog-buttonset" )
6376
+ .appendTo( uiDialogButtonPane );
6377
+
6378
+ // if we already have a button pane, remove it
6379
+ self.uiDialog.find('.ui-dialog-buttonpane').remove();
6380
+
6381
+ if (typeof buttons === 'object' && buttons !== null) {
6382
+ $.each(buttons, function() {
6383
+ return !(hasButtons = true);
6384
+ });
6385
+ }
6386
+ if (hasButtons) {
6387
+ $.each(buttons, function(name, props) {
6388
+ props = $.isFunction( props ) ?
6389
+ { click: props, text: name } :
6390
+ props;
6391
+ var button = $('<button type="button"></button>')
6392
+ .click(function() {
6393
+ props.click.apply(self.element[0], arguments);
6394
+ })
6395
+ .appendTo(uiButtonSet);
6396
+ // can't use .attr( props, true ) with jQuery 1.3.2.
6397
+ $.each( props, function( key, value ) {
6398
+ if ( key === "click" ) {
6399
+ return;
6400
+ }
6401
+ if ( key in attrFn ) {
6402
+ button[ key ]( value );
6403
+ } else {
6404
+ button.attr( key, value );
6405
+ }
6406
+ });
6407
+ if ($.fn.button) {
6408
+ button.button();
6409
+ }
6410
+ });
6411
+ uiDialogButtonPane.appendTo(self.uiDialog);
6412
+ }
6413
+ },
6414
+
6415
+ _makeDraggable: function() {
6416
+ var self = this,
6417
+ options = self.options,
6418
+ doc = $(document),
6419
+ heightBeforeDrag;
6420
+
6421
+ function filteredUi(ui) {
6422
+ return {
6423
+ position: ui.position,
6424
+ offset: ui.offset
6425
+ };
6426
+ }
6427
+
6428
+ self.uiDialog.draggable({
6429
+ cancel: '.ui-dialog-content, .ui-dialog-titlebar-close',
6430
+ handle: '.ui-dialog-titlebar',
6431
+ containment: 'document',
6432
+ start: function(event, ui) {
6433
+ heightBeforeDrag = options.height === "auto" ? "auto" : $(this).height();
6434
+ $(this).height($(this).height()).addClass("ui-dialog-dragging");
6435
+ self._trigger('dragStart', event, filteredUi(ui));
6436
+ },
6437
+ drag: function(event, ui) {
6438
+ self._trigger('drag', event, filteredUi(ui));
6439
+ },
6440
+ stop: function(event, ui) {
6441
+ options.position = [ui.position.left - doc.scrollLeft(),
6442
+ ui.position.top - doc.scrollTop()];
6443
+ $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag);
6444
+ self._trigger('dragStop', event, filteredUi(ui));
6445
+ $.ui.dialog.overlay.resize();
6446
+ }
6447
+ });
6448
+ },
6449
+
6450
+ _makeResizable: function(handles) {
6451
+ handles = (handles === undefined ? this.options.resizable : handles);
6452
+ var self = this,
6453
+ options = self.options,
6454
+ // .ui-resizable has position: relative defined in the stylesheet
6455
+ // but dialogs have to use absolute or fixed positioning
6456
+ position = self.uiDialog.css('position'),
6457
+ resizeHandles = (typeof handles === 'string' ?
6458
+ handles :
6459
+ 'n,e,s,w,se,sw,ne,nw'
6460
+ );
6461
+
6462
+ function filteredUi(ui) {
6463
+ return {
6464
+ originalPosition: ui.originalPosition,
6465
+ originalSize: ui.originalSize,
6466
+ position: ui.position,
6467
+ size: ui.size
6468
+ };
6469
+ }
6470
+
6471
+ self.uiDialog.resizable({
6472
+ cancel: '.ui-dialog-content',
6473
+ containment: 'document',
6474
+ alsoResize: self.element,
6475
+ maxWidth: options.maxWidth,
6476
+ maxHeight: options.maxHeight,
6477
+ minWidth: options.minWidth,
6478
+ minHeight: self._minHeight(),
6479
+ handles: resizeHandles,
6480
+ start: function(event, ui) {
6481
+ $(this).addClass("ui-dialog-resizing");
6482
+ self._trigger('resizeStart', event, filteredUi(ui));
6483
+ },
6484
+ resize: function(event, ui) {
6485
+ self._trigger('resize', event, filteredUi(ui));
6486
+ },
6487
+ stop: function(event, ui) {
6488
+ $(this).removeClass("ui-dialog-resizing");
6489
+ options.height = $(this).height();
6490
+ options.width = $(this).width();
6491
+ self._trigger('resizeStop', event, filteredUi(ui));
6492
+ $.ui.dialog.overlay.resize();
6493
+ }
6494
+ })
6495
+ .css('position', position)
6496
+ .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se');
6497
+ },
6498
+
6499
+ _minHeight: function() {
6500
+ var options = this.options;
6501
+
6502
+ if (options.height === 'auto') {
6503
+ return options.minHeight;
6504
+ } else {
6505
+ return Math.min(options.minHeight, options.height);
6506
+ }
6507
+ },
6508
+
6509
+ _position: function(position) {
6510
+ var myAt = [],
6511
+ offset = [0, 0],
6512
+ isVisible;
6513
+
6514
+ if (position) {
6515
+ // deep extending converts arrays to objects in jQuery <= 1.3.2 :-(
6516
+ // if (typeof position == 'string' || $.isArray(position)) {
6517
+ // myAt = $.isArray(position) ? position : position.split(' ');
6518
+
6519
+ if (typeof position === 'string' || (typeof position === 'object' && '0' in position)) {
6520
+ myAt = position.split ? position.split(' ') : [position[0], position[1]];
6521
+ if (myAt.length === 1) {
6522
+ myAt[1] = myAt[0];
6523
+ }
6524
+
6525
+ $.each(['left', 'top'], function(i, offsetPosition) {
6526
+ if (+myAt[i] === myAt[i]) {
6527
+ offset[i] = myAt[i];
6528
+ myAt[i] = offsetPosition;
6529
+ }
6530
+ });
6531
+
6532
+ position = {
6533
+ my: myAt.join(" "),
6534
+ at: myAt.join(" "),
6535
+ offset: offset.join(" ")
6536
+ };
6537
+ }
6538
+
6539
+ position = $.extend({}, $.ui.dialog.prototype.options.position, position);
6540
+ } else {
6541
+ position = $.ui.dialog.prototype.options.position;
6542
+ }
6543
+
6544
+ // need to show the dialog to get the actual offset in the position plugin
6545
+ isVisible = this.uiDialog.is(':visible');
6546
+ if (!isVisible) {
6547
+ this.uiDialog.show();
6548
+ }
6549
+ this.uiDialog
6550
+ // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
6551
+ .css({ top: 0, left: 0 })
6552
+ .position($.extend({ of: window }, position));
6553
+ if (!isVisible) {
6554
+ this.uiDialog.hide();
6555
+ }
6556
+ },
6557
+
6558
+ _setOptions: function( options ) {
6559
+ var self = this,
6560
+ resizableOptions = {},
6561
+ resize = false;
6562
+
6563
+ $.each( options, function( key, value ) {
6564
+ self._setOption( key, value );
6565
+
6566
+ if ( key in sizeRelatedOptions ) {
6567
+ resize = true;
6568
+ }
6569
+ if ( key in resizableRelatedOptions ) {
6570
+ resizableOptions[ key ] = value;
6571
+ }
6572
+ });
6573
+
6574
+ if ( resize ) {
6575
+ this._size();
6576
+ }
6577
+ if ( this.uiDialog.is( ":data(resizable)" ) ) {
6578
+ this.uiDialog.resizable( "option", resizableOptions );
6579
+ }
6580
+ },
6581
+
6582
+ _setOption: function(key, value){
6583
+ var self = this,
6584
+ uiDialog = self.uiDialog;
6585
+
6586
+ switch (key) {
6587
+ //handling of deprecated beforeclose (vs beforeClose) option
6588
+ //Ticket #4669 http://dev.jqueryui.com/ticket/4669
6589
+ //TODO: remove in 1.9pre
6590
+ case "beforeclose":
6591
+ key = "beforeClose";
6592
+ break;
6593
+ case "buttons":
6594
+ self._createButtons(value);
6595
+ break;
6596
+ case "closeText":
6597
+ // ensure that we always pass a string
6598
+ self.uiDialogTitlebarCloseText.text("" + value);
6599
+ break;
6600
+ case "dialogClass":
6601
+ uiDialog
6602
+ .removeClass(self.options.dialogClass)
6603
+ .addClass(uiDialogClasses + value);
6604
+ break;
6605
+ case "disabled":
6606
+ if (value) {
6607
+ uiDialog.addClass('ui-dialog-disabled');
6608
+ } else {
6609
+ uiDialog.removeClass('ui-dialog-disabled');
6610
+ }
6611
+ break;
6612
+ case "draggable":
6613
+ var isDraggable = uiDialog.is( ":data(draggable)" );
6614
+ if ( isDraggable && !value ) {
6615
+ uiDialog.draggable( "destroy" );
6616
+ }
6617
+
6618
+ if ( !isDraggable && value ) {
6619
+ self._makeDraggable();
6620
+ }
6621
+ break;
6622
+ case "position":
6623
+ self._position(value);
6624
+ break;
6625
+ case "resizable":
6626
+ // currently resizable, becoming non-resizable
6627
+ var isResizable = uiDialog.is( ":data(resizable)" );
6628
+ if (isResizable && !value) {
6629
+ uiDialog.resizable('destroy');
6630
+ }
6631
+
6632
+ // currently resizable, changing handles
6633
+ if (isResizable && typeof value === 'string') {
6634
+ uiDialog.resizable('option', 'handles', value);
6635
+ }
6636
+
6637
+ // currently non-resizable, becoming resizable
6638
+ if (!isResizable && value !== false) {
6639
+ self._makeResizable(value);
6640
+ }
6641
+ break;
6642
+ case "title":
6643
+ // convert whatever was passed in o a string, for html() to not throw up
6644
+ $(".ui-dialog-title", self.uiDialogTitlebar).html("" + (value || '&#160;'));
6645
+ break;
6646
+ }
6647
+
6648
+ $.Widget.prototype._setOption.apply(self, arguments);
6649
+ },
6650
+
6651
+ _size: function() {
6652
+ /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
6653
+ * divs will both have width and height set, so we need to reset them
6654
+ */
6655
+ var options = this.options,
6656
+ nonContentHeight,
6657
+ minContentHeight,
6658
+ isVisible = this.uiDialog.is( ":visible" );
6659
+
6660
+ // reset content sizing
6661
+ this.element.show().css({
6662
+ width: 'auto',
6663
+ minHeight: 0,
6664
+ height: 0
6665
+ });
6666
+
6667
+ if (options.minWidth > options.width) {
6668
+ options.width = options.minWidth;
6669
+ }
6670
+
6671
+ // reset wrapper sizing
6672
+ // determine the height of all the non-content elements
6673
+ nonContentHeight = this.uiDialog.css({
6674
+ height: 'auto',
6675
+ width: options.width
6676
+ })
6677
+ .height();
6678
+ minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
6679
+
6680
+ if ( options.height === "auto" ) {
6681
+ // only needed for IE6 support
6682
+ if ( $.support.minHeight ) {
6683
+ this.element.css({
6684
+ minHeight: minContentHeight,
6685
+ height: "auto"
6686
+ });
6687
+ } else {
6688
+ this.uiDialog.show();
6689
+ var autoHeight = this.element.css( "height", "auto" ).height();
6690
+ if ( !isVisible ) {
6691
+ this.uiDialog.hide();
6692
+ }
6693
+ this.element.height( Math.max( autoHeight, minContentHeight ) );
6694
+ }
6695
+ } else {
6696
+ this.element.height( Math.max( options.height - nonContentHeight, 0 ) );
6697
+ }
6698
+
6699
+ if (this.uiDialog.is(':data(resizable)')) {
6700
+ this.uiDialog.resizable('option', 'minHeight', this._minHeight());
6701
+ }
6702
+ }
6703
+ });
6704
+
6705
+ $.extend($.ui.dialog, {
6706
+ version: "1.8.17",
6707
+
6708
+ uuid: 0,
6709
+ maxZ: 0,
6710
+
6711
+ getTitleId: function($el) {
6712
+ var id = $el.attr('id');
6713
+ if (!id) {
6714
+ this.uuid += 1;
6715
+ id = this.uuid;
6716
+ }
6717
+ return 'ui-dialog-title-' + id;
6718
+ },
6719
+
6720
+ overlay: function(dialog) {
6721
+ this.$el = $.ui.dialog.overlay.create(dialog);
6722
+ }
6723
+ });
6724
+
6725
+ $.extend($.ui.dialog.overlay, {
6726
+ instances: [],
6727
+ // reuse old instances due to IE memory leak with alpha transparency (see #5185)
6728
+ oldInstances: [],
6729
+ maxZ: 0,
6730
+ events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
6731
+ function(event) { return event + '.dialog-overlay'; }).join(' '),
6732
+ create: function(dialog) {
6733
+ if (this.instances.length === 0) {
6734
+ // prevent use of anchors and inputs
6735
+ // we use a setTimeout in case the overlay is created from an
6736
+ // event that we're going to be cancelling (see #2804)
6737
+ setTimeout(function() {
6738
+ // handle $(el).dialog().dialog('close') (see #4065)
6739
+ if ($.ui.dialog.overlay.instances.length) {
6740
+ $(document).bind($.ui.dialog.overlay.events, function(event) {
6741
+ // stop events if the z-index of the target is < the z-index of the overlay
6742
+ // we cannot return true when we don't want to cancel the event (#3523)
6743
+ if ($(event.target).zIndex() < $.ui.dialog.overlay.maxZ) {
6744
+ return false;
6745
+ }
6746
+ });
6747
+ }
6748
+ }, 1);
6749
+
6750
+ // allow closing by pressing the escape key
6751
+ $(document).bind('keydown.dialog-overlay', function(event) {
6752
+ if (dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
6753
+ event.keyCode === $.ui.keyCode.ESCAPE) {
6754
+
6755
+ dialog.close(event);
6756
+ event.preventDefault();
6757
+ }
6758
+ });
6759
+
6760
+ // handle window resize
6761
+ $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
6762
+ }
6763
+
6764
+ var $el = (this.oldInstances.pop() || $('<div></div>').addClass('ui-widget-overlay'))
6765
+ .appendTo(document.body)
6766
+ .css({
6767
+ width: this.width(),
6768
+ height: this.height()
6769
+ });
6770
+
6771
+ if ($.fn.bgiframe) {
6772
+ $el.bgiframe();
6773
+ }
6774
+
6775
+ this.instances.push($el);
6776
+ return $el;
6777
+ },
6778
+
6779
+ destroy: function($el) {
6780
+ var indexOf = $.inArray($el, this.instances);
6781
+ if (indexOf != -1){
6782
+ this.oldInstances.push(this.instances.splice(indexOf, 1)[0]);
6783
+ }
6784
+
6785
+ if (this.instances.length === 0) {
6786
+ $([document, window]).unbind('.dialog-overlay');
6787
+ }
6788
+
6789
+ $el.remove();
6790
+
6791
+ // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
6792
+ var maxZ = 0;
6793
+ $.each(this.instances, function() {
6794
+ maxZ = Math.max(maxZ, this.css('z-index'));
6795
+ });
6796
+ this.maxZ = maxZ;
6797
+ },
6798
+
6799
+ height: function() {
6800
+ var scrollHeight,
6801
+ offsetHeight;
6802
+ // handle IE 6
6803
+ if ($.browser.msie && $.browser.version < 7) {
6804
+ scrollHeight = Math.max(
6805
+ document.documentElement.scrollHeight,
6806
+ document.body.scrollHeight
6807
+ );
6808
+ offsetHeight = Math.max(
6809
+ document.documentElement.offsetHeight,
6810
+ document.body.offsetHeight
6811
+ );
6812
+
6813
+ if (scrollHeight < offsetHeight) {
6814
+ return $(window).height() + 'px';
6815
+ } else {
6816
+ return scrollHeight + 'px';
6817
+ }
6818
+ // handle "good" browsers
6819
+ } else {
6820
+ return $(document).height() + 'px';
6821
+ }
6822
+ },
6823
+
6824
+ width: function() {
6825
+ var scrollWidth,
6826
+ offsetWidth;
6827
+ // handle IE
6828
+ if ( $.browser.msie ) {
6829
+ scrollWidth = Math.max(
6830
+ document.documentElement.scrollWidth,
6831
+ document.body.scrollWidth
6832
+ );
6833
+ offsetWidth = Math.max(
6834
+ document.documentElement.offsetWidth,
6835
+ document.body.offsetWidth
6836
+ );
6837
+
6838
+ if (scrollWidth < offsetWidth) {
6839
+ return $(window).width() + 'px';
6840
+ } else {
6841
+ return scrollWidth + 'px';
6842
+ }
6843
+ // handle "good" browsers
6844
+ } else {
6845
+ return $(document).width() + 'px';
6846
+ }
6847
+ },
6848
+
6849
+ resize: function() {
6850
+ /* If the dialog is draggable and the user drags it past the
6851
+ * right edge of the window, the document becomes wider so we
6852
+ * need to stretch the overlay. If the user then drags the
6853
+ * dialog back to the left, the document will become narrower,
6854
+ * so we need to shrink the overlay to the appropriate size.
6855
+ * This is handled by shrinking the overlay before setting it
6856
+ * to the full document size.
6857
+ */
6858
+ var $overlays = $([]);
6859
+ $.each($.ui.dialog.overlay.instances, function() {
6860
+ $overlays = $overlays.add(this);
6861
+ });
6862
+
6863
+ $overlays.css({
6864
+ width: 0,
6865
+ height: 0
6866
+ }).css({
6867
+ width: $.ui.dialog.overlay.width(),
6868
+ height: $.ui.dialog.overlay.height()
6869
+ });
6870
+ }
6871
+ });
6872
+
6873
+ $.extend($.ui.dialog.overlay.prototype, {
6874
+ destroy: function() {
6875
+ $.ui.dialog.overlay.destroy(this.$el);
6876
+ }
6877
+ });
6878
+
6879
+ }(jQuery));
6880
+ /*
6881
+ * jQuery UI Slider 1.8.17
6882
+ *
6883
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
6884
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6885
+ * http://jquery.org/license
6886
+ *
6887
+ * http://docs.jquery.com/UI/Slider
6888
+ *
6889
+ * Depends:
6890
+ * jquery.ui.core.js
6891
+ * jquery.ui.mouse.js
6892
+ * jquery.ui.widget.js
6893
+ */
6894
+ (function( $, undefined ) {
6895
+
6896
+ // number of pages in a slider
6897
+ // (how many times can you page up/down to go through the whole range)
6898
+ var numPages = 5;
6899
+
6900
+ $.widget( "ui.slider", $.ui.mouse, {
6901
+
6902
+ widgetEventPrefix: "slide",
6903
+
6904
+ options: {
6905
+ animate: false,
6906
+ distance: 0,
6907
+ max: 100,
6908
+ min: 0,
6909
+ orientation: "horizontal",
6910
+ range: false,
6911
+ step: 1,
6912
+ value: 0,
6913
+ values: null
6914
+ },
6915
+
6916
+ _create: function() {
6917
+ var self = this,
6918
+ o = this.options,
6919
+ existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
6920
+ handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
6921
+ handleCount = ( o.values && o.values.length ) || 1,
6922
+ handles = [];
6923
+
6924
+ this._keySliding = false;
6925
+ this._mouseSliding = false;
6926
+ this._animateOff = true;
6927
+ this._handleIndex = null;
6928
+ this._detectOrientation();
6929
+ this._mouseInit();
6930
+
6931
+ this.element
6932
+ .addClass( "ui-slider" +
6933
+ " ui-slider-" + this.orientation +
6934
+ " ui-widget" +
6935
+ " ui-widget-content" +
6936
+ " ui-corner-all" +
6937
+ ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) );
6938
+
6939
+ this.range = $([]);
6940
+
6941
+ if ( o.range ) {
6942
+ if ( o.range === true ) {
6943
+ if ( !o.values ) {
6944
+ o.values = [ this._valueMin(), this._valueMin() ];
6945
+ }
6946
+ if ( o.values.length && o.values.length !== 2 ) {
6947
+ o.values = [ o.values[0], o.values[0] ];
6948
+ }
6949
+ }
6950
+
6951
+ this.range = $( "<div></div>" )
6952
+ .appendTo( this.element )
6953
+ .addClass( "ui-slider-range" +
6954
+ // note: this isn't the most fittingly semantic framework class for this element,
6955
+ // but worked best visually with a variety of themes
6956
+ " ui-widget-header" +
6957
+ ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) );
6958
+ }
6959
+
6960
+ for ( var i = existingHandles.length; i < handleCount; i += 1 ) {
6961
+ handles.push( handle );
6962
+ }
6963
+
6964
+ this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( self.element ) );
6965
+
6966
+ this.handle = this.handles.eq( 0 );
6967
+
6968
+ this.handles.add( this.range ).filter( "a" )
6969
+ .click(function( event ) {
6970
+ event.preventDefault();
6971
+ })
6972
+ .hover(function() {
6973
+ if ( !o.disabled ) {
6974
+ $( this ).addClass( "ui-state-hover" );
6975
+ }
6976
+ }, function() {
6977
+ $( this ).removeClass( "ui-state-hover" );
6978
+ })
6979
+ .focus(function() {
6980
+ if ( !o.disabled ) {
6981
+ $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
6982
+ $( this ).addClass( "ui-state-focus" );
6983
+ } else {
6984
+ $( this ).blur();
6985
+ }
6986
+ })
6987
+ .blur(function() {
6988
+ $( this ).removeClass( "ui-state-focus" );
6989
+ });
6990
+
6991
+ this.handles.each(function( i ) {
6992
+ $( this ).data( "index.ui-slider-handle", i );
6993
+ });
6994
+
6995
+ this.handles
6996
+ .keydown(function( event ) {
6997
+ var ret = true,
6998
+ index = $( this ).data( "index.ui-slider-handle" ),
6999
+ allowed,
7000
+ curVal,
7001
+ newVal,
7002
+ step;
7003
+
7004
+ if ( self.options.disabled ) {
7005
+ return;
7006
+ }
7007
+
7008
+ switch ( event.keyCode ) {
7009
+ case $.ui.keyCode.HOME:
7010
+ case $.ui.keyCode.END:
7011
+ case $.ui.keyCode.PAGE_UP:
7012
+ case $.ui.keyCode.PAGE_DOWN:
7013
+ case $.ui.keyCode.UP:
7014
+ case $.ui.keyCode.RIGHT:
7015
+ case $.ui.keyCode.DOWN:
7016
+ case $.ui.keyCode.LEFT:
7017
+ ret = false;
7018
+ if ( !self._keySliding ) {
7019
+ self._keySliding = true;
7020
+ $( this ).addClass( "ui-state-active" );
7021
+ allowed = self._start( event, index );
7022
+ if ( allowed === false ) {
7023
+ return;
7024
+ }
7025
+ }
7026
+ break;
7027
+ }
7028
+
7029
+ step = self.options.step;
7030
+ if ( self.options.values && self.options.values.length ) {
7031
+ curVal = newVal = self.values( index );
7032
+ } else {
7033
+ curVal = newVal = self.value();
7034
+ }
7035
+
7036
+ switch ( event.keyCode ) {
7037
+ case $.ui.keyCode.HOME:
7038
+ newVal = self._valueMin();
7039
+ break;
7040
+ case $.ui.keyCode.END:
7041
+ newVal = self._valueMax();
7042
+ break;
7043
+ case $.ui.keyCode.PAGE_UP:
7044
+ newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) );
7045
+ break;
7046
+ case $.ui.keyCode.PAGE_DOWN:
7047
+ newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) );
7048
+ break;
7049
+ case $.ui.keyCode.UP:
7050
+ case $.ui.keyCode.RIGHT:
7051
+ if ( curVal === self._valueMax() ) {
7052
+ return;
7053
+ }
7054
+ newVal = self._trimAlignValue( curVal + step );
7055
+ break;
7056
+ case $.ui.keyCode.DOWN:
7057
+ case $.ui.keyCode.LEFT:
7058
+ if ( curVal === self._valueMin() ) {
7059
+ return;
7060
+ }
7061
+ newVal = self._trimAlignValue( curVal - step );
7062
+ break;
7063
+ }
7064
+
7065
+ self._slide( event, index, newVal );
7066
+
7067
+ return ret;
7068
+
7069
+ })
7070
+ .keyup(function( event ) {
7071
+ var index = $( this ).data( "index.ui-slider-handle" );
7072
+
7073
+ if ( self._keySliding ) {
7074
+ self._keySliding = false;
7075
+ self._stop( event, index );
7076
+ self._change( event, index );
7077
+ $( this ).removeClass( "ui-state-active" );
7078
+ }
7079
+
7080
+ });
7081
+
7082
+ this._refreshValue();
7083
+
7084
+ this._animateOff = false;
7085
+ },
7086
+
7087
+ destroy: function() {
7088
+ this.handles.remove();
7089
+ this.range.remove();
7090
+
7091
+ this.element
7092
+ .removeClass( "ui-slider" +
7093
+ " ui-slider-horizontal" +
7094
+ " ui-slider-vertical" +
7095
+ " ui-slider-disabled" +
7096
+ " ui-widget" +
7097
+ " ui-widget-content" +
7098
+ " ui-corner-all" )
7099
+ .removeData( "slider" )
7100
+ .unbind( ".slider" );
7101
+
7102
+ this._mouseDestroy();
7103
+
7104
+ return this;
7105
+ },
7106
+
7107
+ _mouseCapture: function( event ) {
7108
+ var o = this.options,
7109
+ position,
7110
+ normValue,
7111
+ distance,
7112
+ closestHandle,
7113
+ self,
7114
+ index,
7115
+ allowed,
7116
+ offset,
7117
+ mouseOverHandle;
7118
+
7119
+ if ( o.disabled ) {
7120
+ return false;
7121
+ }
7122
+
7123
+ this.elementSize = {
7124
+ width: this.element.outerWidth(),
7125
+ height: this.element.outerHeight()
7126
+ };
7127
+ this.elementOffset = this.element.offset();
7128
+
7129
+ position = { x: event.pageX, y: event.pageY };
7130
+ normValue = this._normValueFromMouse( position );
7131
+ distance = this._valueMax() - this._valueMin() + 1;
7132
+ self = this;
7133
+ this.handles.each(function( i ) {
7134
+ var thisDistance = Math.abs( normValue - self.values(i) );
7135
+ if ( distance > thisDistance ) {
7136
+ distance = thisDistance;
7137
+ closestHandle = $( this );
7138
+ index = i;
7139
+ }
7140
+ });
7141
+
7142
+ // workaround for bug #3736 (if both handles of a range are at 0,
7143
+ // the first is always used as the one with least distance,
7144
+ // and moving it is obviously prevented by preventing negative ranges)
7145
+ if( o.range === true && this.values(1) === o.min ) {
7146
+ index += 1;
7147
+ closestHandle = $( this.handles[index] );
7148
+ }
7149
+
7150
+ allowed = this._start( event, index );
7151
+ if ( allowed === false ) {
7152
+ return false;
7153
+ }
7154
+ this._mouseSliding = true;
7155
+
7156
+ self._handleIndex = index;
7157
+
7158
+ closestHandle
7159
+ .addClass( "ui-state-active" )
7160
+ .focus();
7161
+
7162
+ offset = closestHandle.offset();
7163
+ mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" );
7164
+ this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
7165
+ left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
7166
+ top: event.pageY - offset.top -
7167
+ ( closestHandle.height() / 2 ) -
7168
+ ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
7169
+ ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
7170
+ ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
7171
+ };
7172
+
7173
+ if ( !this.handles.hasClass( "ui-state-hover" ) ) {
7174
+ this._slide( event, index, normValue );
7175
+ }
7176
+ this._animateOff = true;
7177
+ return true;
7178
+ },
7179
+
7180
+ _mouseStart: function( event ) {
7181
+ return true;
7182
+ },
7183
+
7184
+ _mouseDrag: function( event ) {
7185
+ var position = { x: event.pageX, y: event.pageY },
7186
+ normValue = this._normValueFromMouse( position );
7187
+
7188
+ this._slide( event, this._handleIndex, normValue );
7189
+
7190
+ return false;
7191
+ },
7192
+
7193
+ _mouseStop: function( event ) {
7194
+ this.handles.removeClass( "ui-state-active" );
7195
+ this._mouseSliding = false;
7196
+
7197
+ this._stop( event, this._handleIndex );
7198
+ this._change( event, this._handleIndex );
7199
+
7200
+ this._handleIndex = null;
7201
+ this._clickOffset = null;
7202
+ this._animateOff = false;
7203
+
7204
+ return false;
7205
+ },
7206
+
7207
+ _detectOrientation: function() {
7208
+ this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
7209
+ },
7210
+
7211
+ _normValueFromMouse: function( position ) {
7212
+ var pixelTotal,
7213
+ pixelMouse,
7214
+ percentMouse,
7215
+ valueTotal,
7216
+ valueMouse;
7217
+
7218
+ if ( this.orientation === "horizontal" ) {
7219
+ pixelTotal = this.elementSize.width;
7220
+ pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
7221
+ } else {
7222
+ pixelTotal = this.elementSize.height;
7223
+ pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
7224
+ }
7225
+
7226
+ percentMouse = ( pixelMouse / pixelTotal );
7227
+ if ( percentMouse > 1 ) {
7228
+ percentMouse = 1;
7229
+ }
7230
+ if ( percentMouse < 0 ) {
7231
+ percentMouse = 0;
7232
+ }
7233
+ if ( this.orientation === "vertical" ) {
7234
+ percentMouse = 1 - percentMouse;
7235
+ }
7236
+
7237
+ valueTotal = this._valueMax() - this._valueMin();
7238
+ valueMouse = this._valueMin() + percentMouse * valueTotal;
7239
+
7240
+ return this._trimAlignValue( valueMouse );
7241
+ },
7242
+
7243
+ _start: function( event, index ) {
7244
+ var uiHash = {
7245
+ handle: this.handles[ index ],
7246
+ value: this.value()
7247
+ };
7248
+ if ( this.options.values && this.options.values.length ) {
7249
+ uiHash.value = this.values( index );
7250
+ uiHash.values = this.values();
7251
+ }
7252
+ return this._trigger( "start", event, uiHash );
7253
+ },
7254
+
7255
+ _slide: function( event, index, newVal ) {
7256
+ var otherVal,
7257
+ newValues,
7258
+ allowed;
7259
+
7260
+ if ( this.options.values && this.options.values.length ) {
7261
+ otherVal = this.values( index ? 0 : 1 );
7262
+
7263
+ if ( ( this.options.values.length === 2 && this.options.range === true ) &&
7264
+ ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
7265
+ ) {
7266
+ newVal = otherVal;
7267
+ }
7268
+
7269
+ if ( newVal !== this.values( index ) ) {
7270
+ newValues = this.values();
7271
+ newValues[ index ] = newVal;
7272
+ // A slide can be canceled by returning false from the slide callback
7273
+ allowed = this._trigger( "slide", event, {
7274
+ handle: this.handles[ index ],
7275
+ value: newVal,
7276
+ values: newValues
7277
+ } );
7278
+ otherVal = this.values( index ? 0 : 1 );
7279
+ if ( allowed !== false ) {
7280
+ this.values( index, newVal, true );
7281
+ }
7282
+ }
7283
+ } else {
7284
+ if ( newVal !== this.value() ) {
7285
+ // A slide can be canceled by returning false from the slide callback
7286
+ allowed = this._trigger( "slide", event, {
7287
+ handle: this.handles[ index ],
7288
+ value: newVal
7289
+ } );
7290
+ if ( allowed !== false ) {
7291
+ this.value( newVal );
7292
+ }
7293
+ }
7294
+ }
7295
+ },
7296
+
7297
+ _stop: function( event, index ) {
7298
+ var uiHash = {
7299
+ handle: this.handles[ index ],
7300
+ value: this.value()
7301
+ };
7302
+ if ( this.options.values && this.options.values.length ) {
7303
+ uiHash.value = this.values( index );
7304
+ uiHash.values = this.values();
7305
+ }
7306
+
7307
+ this._trigger( "stop", event, uiHash );
7308
+ },
7309
+
7310
+ _change: function( event, index ) {
7311
+ if ( !this._keySliding && !this._mouseSliding ) {
7312
+ var uiHash = {
7313
+ handle: this.handles[ index ],
7314
+ value: this.value()
7315
+ };
7316
+ if ( this.options.values && this.options.values.length ) {
7317
+ uiHash.value = this.values( index );
7318
+ uiHash.values = this.values();
7319
+ }
7320
+
7321
+ this._trigger( "change", event, uiHash );
7322
+ }
7323
+ },
7324
+
7325
+ value: function( newValue ) {
7326
+ if ( arguments.length ) {
7327
+ this.options.value = this._trimAlignValue( newValue );
7328
+ this._refreshValue();
7329
+ this._change( null, 0 );
7330
+ return;
7331
+ }
7332
+
7333
+ return this._value();
7334
+ },
7335
+
7336
+ values: function( index, newValue ) {
7337
+ var vals,
7338
+ newValues,
7339
+ i;
7340
+
7341
+ if ( arguments.length > 1 ) {
7342
+ this.options.values[ index ] = this._trimAlignValue( newValue );
7343
+ this._refreshValue();
7344
+ this._change( null, index );
7345
+ return;
7346
+ }
7347
+
7348
+ if ( arguments.length ) {
7349
+ if ( $.isArray( arguments[ 0 ] ) ) {
7350
+ vals = this.options.values;
7351
+ newValues = arguments[ 0 ];
7352
+ for ( i = 0; i < vals.length; i += 1 ) {
7353
+ vals[ i ] = this._trimAlignValue( newValues[ i ] );
7354
+ this._change( null, i );
7355
+ }
7356
+ this._refreshValue();
7357
+ } else {
7358
+ if ( this.options.values && this.options.values.length ) {
7359
+ return this._values( index );
7360
+ } else {
7361
+ return this.value();
7362
+ }
7363
+ }
7364
+ } else {
7365
+ return this._values();
7366
+ }
7367
+ },
7368
+
7369
+ _setOption: function( key, value ) {
7370
+ var i,
7371
+ valsLength = 0;
7372
+
7373
+ if ( $.isArray( this.options.values ) ) {
7374
+ valsLength = this.options.values.length;
7375
+ }
7376
+
7377
+ $.Widget.prototype._setOption.apply( this, arguments );
7378
+
7379
+ switch ( key ) {
7380
+ case "disabled":
7381
+ if ( value ) {
7382
+ this.handles.filter( ".ui-state-focus" ).blur();
7383
+ this.handles.removeClass( "ui-state-hover" );
7384
+ this.handles.propAttr( "disabled", true );
7385
+ this.element.addClass( "ui-disabled" );
7386
+ } else {
7387
+ this.handles.propAttr( "disabled", false );
7388
+ this.element.removeClass( "ui-disabled" );
7389
+ }
7390
+ break;
7391
+ case "orientation":
7392
+ this._detectOrientation();
7393
+ this.element
7394
+ .removeClass( "ui-slider-horizontal ui-slider-vertical" )
7395
+ .addClass( "ui-slider-" + this.orientation );
7396
+ this._refreshValue();
7397
+ break;
7398
+ case "value":
7399
+ this._animateOff = true;
7400
+ this._refreshValue();
7401
+ this._change( null, 0 );
7402
+ this._animateOff = false;
7403
+ break;
7404
+ case "values":
7405
+ this._animateOff = true;
7406
+ this._refreshValue();
7407
+ for ( i = 0; i < valsLength; i += 1 ) {
7408
+ this._change( null, i );
7409
+ }
7410
+ this._animateOff = false;
7411
+ break;
7412
+ }
7413
+ },
7414
+
7415
+ //internal value getter
7416
+ // _value() returns value trimmed by min and max, aligned by step
7417
+ _value: function() {
7418
+ var val = this.options.value;
7419
+ val = this._trimAlignValue( val );
7420
+
7421
+ return val;
7422
+ },
7423
+
7424
+ //internal values getter
7425
+ // _values() returns array of values trimmed by min and max, aligned by step
7426
+ // _values( index ) returns single value trimmed by min and max, aligned by step
7427
+ _values: function( index ) {
7428
+ var val,
7429
+ vals,
7430
+ i;
7431
+
7432
+ if ( arguments.length ) {
7433
+ val = this.options.values[ index ];
7434
+ val = this._trimAlignValue( val );
7435
+
7436
+ return val;
7437
+ } else {
7438
+ // .slice() creates a copy of the array
7439
+ // this copy gets trimmed by min and max and then returned
7440
+ vals = this.options.values.slice();
7441
+ for ( i = 0; i < vals.length; i+= 1) {
7442
+ vals[ i ] = this._trimAlignValue( vals[ i ] );
7443
+ }
7444
+
7445
+ return vals;
7446
+ }
7447
+ },
7448
+
7449
+ // returns the step-aligned value that val is closest to, between (inclusive) min and max
7450
+ _trimAlignValue: function( val ) {
7451
+ if ( val <= this._valueMin() ) {
7452
+ return this._valueMin();
7453
+ }
7454
+ if ( val >= this._valueMax() ) {
7455
+ return this._valueMax();
7456
+ }
7457
+ var step = ( this.options.step > 0 ) ? this.options.step : 1,
7458
+ valModStep = (val - this._valueMin()) % step,
7459
+ alignValue = val - valModStep;
7460
+
7461
+ if ( Math.abs(valModStep) * 2 >= step ) {
7462
+ alignValue += ( valModStep > 0 ) ? step : ( -step );
7463
+ }
7464
+
7465
+ // Since JavaScript has problems with large floats, round
7466
+ // the final value to 5 digits after the decimal point (see #4124)
7467
+ return parseFloat( alignValue.toFixed(5) );
7468
+ },
7469
+
7470
+ _valueMin: function() {
7471
+ return this.options.min;
7472
+ },
7473
+
7474
+ _valueMax: function() {
7475
+ return this.options.max;
7476
+ },
7477
+
7478
+ _refreshValue: function() {
7479
+ var oRange = this.options.range,
7480
+ o = this.options,
7481
+ self = this,
7482
+ animate = ( !this._animateOff ) ? o.animate : false,
7483
+ valPercent,
7484
+ _set = {},
7485
+ lastValPercent,
7486
+ value,
7487
+ valueMin,
7488
+ valueMax;
7489
+
7490
+ if ( this.options.values && this.options.values.length ) {
7491
+ this.handles.each(function( i, j ) {
7492
+ valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100;
7493
+ _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
7494
+ $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
7495
+ if ( self.options.range === true ) {
7496
+ if ( self.orientation === "horizontal" ) {
7497
+ if ( i === 0 ) {
7498
+ self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
7499
+ }
7500
+ if ( i === 1 ) {
7501
+ self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
7502
+ }
7503
+ } else {
7504
+ if ( i === 0 ) {
7505
+ self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
7506
+ }
7507
+ if ( i === 1 ) {
7508
+ self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
7509
+ }
7510
+ }
7511
+ }
7512
+ lastValPercent = valPercent;
7513
+ });
7514
+ } else {
7515
+ value = this.value();
7516
+ valueMin = this._valueMin();
7517
+ valueMax = this._valueMax();
7518
+ valPercent = ( valueMax !== valueMin ) ?
7519
+ ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
7520
+ 0;
7521
+ _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
7522
+ this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
7523
+
7524
+ if ( oRange === "min" && this.orientation === "horizontal" ) {
7525
+ this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
7526
+ }
7527
+ if ( oRange === "max" && this.orientation === "horizontal" ) {
7528
+ this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
7529
+ }
7530
+ if ( oRange === "min" && this.orientation === "vertical" ) {
7531
+ this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
7532
+ }
7533
+ if ( oRange === "max" && this.orientation === "vertical" ) {
7534
+ this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
7535
+ }
7536
+ }
7537
+ }
7538
+
7539
+ });
7540
+
7541
+ $.extend( $.ui.slider, {
7542
+ version: "1.8.17"
7543
+ });
7544
+
7545
+ }(jQuery));
7546
+ /*
7547
+ * jQuery UI Tabs 1.8.17
7548
+ *
7549
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
7550
+ * Dual licensed under the MIT or GPL Version 2 licenses.
7551
+ * http://jquery.org/license
7552
+ *
7553
+ * http://docs.jquery.com/UI/Tabs
7554
+ *
7555
+ * Depends:
7556
+ * jquery.ui.core.js
7557
+ * jquery.ui.widget.js
7558
+ */
7559
+ (function( $, undefined ) {
7560
+
7561
+ var tabId = 0,
7562
+ listId = 0;
7563
+
7564
+ function getNextTabId() {
7565
+ return ++tabId;
7566
+ }
7567
+
7568
+ function getNextListId() {
7569
+ return ++listId;
7570
+ }
7571
+
7572
+ $.widget( "ui.tabs", {
7573
+ options: {
7574
+ add: null,
7575
+ ajaxOptions: null,
7576
+ cache: false,
7577
+ cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
7578
+ collapsible: false,
7579
+ disable: null,
7580
+ disabled: [],
7581
+ enable: null,
7582
+ event: "click",
7583
+ fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
7584
+ idPrefix: "ui-tabs-",
7585
+ load: null,
7586
+ panelTemplate: "<div></div>",
7587
+ remove: null,
7588
+ select: null,
7589
+ show: null,
7590
+ spinner: "<em>Loading&#8230;</em>",
7591
+ tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
7592
+ },
7593
+
7594
+ _create: function() {
7595
+ this._tabify( true );
7596
+ },
7597
+
7598
+ _setOption: function( key, value ) {
7599
+ if ( key == "selected" ) {
7600
+ if (this.options.collapsible && value == this.options.selected ) {
7601
+ return;
7602
+ }
7603
+ this.select( value );
7604
+ } else {
7605
+ this.options[ key ] = value;
7606
+ this._tabify();
7607
+ }
7608
+ },
7609
+
7610
+ _tabId: function( a ) {
7611
+ return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) ||
7612
+ this.options.idPrefix + getNextTabId();
7613
+ },
7614
+
7615
+ _sanitizeSelector: function( hash ) {
7616
+ // we need this because an id may contain a ":"
7617
+ return hash.replace( /:/g, "\\:" );
7618
+ },
7619
+
7620
+ _cookie: function() {
7621
+ var cookie = this.cookie ||
7622
+ ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() );
7623
+ return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) );
7624
+ },
7625
+
7626
+ _ui: function( tab, panel ) {
7627
+ return {
7628
+ tab: tab,
7629
+ panel: panel,
7630
+ index: this.anchors.index( tab )
7631
+ };
7632
+ },
7633
+
7634
+ _cleanup: function() {
7635
+ // restore all former loading tabs labels
7636
+ this.lis.filter( ".ui-state-processing" )
7637
+ .removeClass( "ui-state-processing" )
7638
+ .find( "span:data(label.tabs)" )
7639
+ .each(function() {
7640
+ var el = $( this );
7641
+ el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" );
7642
+ });
7643
+ },
7644
+
7645
+ _tabify: function( init ) {
7646
+ var self = this,
7647
+ o = this.options,
7648
+ fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
7649
+
7650
+ this.list = this.element.find( "ol,ul" ).eq( 0 );
7651
+ this.lis = $( " > li:has(a[href])", this.list );
7652
+ this.anchors = this.lis.map(function() {
7653
+ return $( "a", this )[ 0 ];
7654
+ });
7655
+ this.panels = $( [] );
7656
+
7657
+ this.anchors.each(function( i, a ) {
7658
+ var href = $( a ).attr( "href" );
7659
+ // For dynamically created HTML that contains a hash as href IE < 8 expands
7660
+ // such href to the full page url with hash and then misinterprets tab as ajax.
7661
+ // Same consideration applies for an added tab with a fragment identifier
7662
+ // since a[href=#fragment-identifier] does unexpectedly not match.
7663
+ // Thus normalize href attribute...
7664
+ var hrefBase = href.split( "#" )[ 0 ],
7665
+ baseEl;
7666
+ if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] ||
7667
+ ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) {
7668
+ href = a.hash;
7669
+ a.href = href;
7670
+ }
7671
+
7672
+ // inline tab
7673
+ if ( fragmentId.test( href ) ) {
7674
+ self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) );
7675
+ // remote tab
7676
+ // prevent loading the page itself if href is just "#"
7677
+ } else if ( href && href !== "#" ) {
7678
+ // required for restore on destroy
7679
+ $.data( a, "href.tabs", href );
7680
+
7681
+ // TODO until #3808 is fixed strip fragment identifier from url
7682
+ // (IE fails to load from such url)
7683
+ $.data( a, "load.tabs", href.replace( /#.*$/, "" ) );
7684
+
7685
+ var id = self._tabId( a );
7686
+ a.href = "#" + id;
7687
+ var $panel = self.element.find( "#" + id );
7688
+ if ( !$panel.length ) {
7689
+ $panel = $( o.panelTemplate )
7690
+ .attr( "id", id )
7691
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
7692
+ .insertAfter( self.panels[ i - 1 ] || self.list );
7693
+ $panel.data( "destroy.tabs", true );
7694
+ }
7695
+ self.panels = self.panels.add( $panel );
7696
+ // invalid tab href
7697
+ } else {
7698
+ o.disabled.push( i );
7699
+ }
7700
+ });
7701
+
7702
+ // initialization from scratch
7703
+ if ( init ) {
7704
+ // attach necessary classes for styling
7705
+ this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
7706
+ this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
7707
+ this.lis.addClass( "ui-state-default ui-corner-top" );
7708
+ this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );
7709
+
7710
+ // Selected tab
7711
+ // use "selected" option or try to retrieve:
7712
+ // 1. from fragment identifier in url
7713
+ // 2. from cookie
7714
+ // 3. from selected class attribute on <li>
7715
+ if ( o.selected === undefined ) {
7716
+ if ( location.hash ) {
7717
+ this.anchors.each(function( i, a ) {
7718
+ if ( a.hash == location.hash ) {
7719
+ o.selected = i;
7720
+ return false;
7721
+ }
7722
+ });
7723
+ }
7724
+ if ( typeof o.selected !== "number" && o.cookie ) {
7725
+ o.selected = parseInt( self._cookie(), 10 );
7726
+ }
7727
+ if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) {
7728
+ o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
7729
+ }
7730
+ o.selected = o.selected || ( this.lis.length ? 0 : -1 );
7731
+ } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release
7732
+ o.selected = -1;
7733
+ }
7734
+
7735
+ // sanity check - default to first tab...
7736
+ o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 )
7737
+ ? o.selected
7738
+ : 0;
7739
+
7740
+ // Take disabling tabs via class attribute from HTML
7741
+ // into account and update option properly.
7742
+ // A selected tab cannot become disabled.
7743
+ o.disabled = $.unique( o.disabled.concat(
7744
+ $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) {
7745
+ return self.lis.index( n );
7746
+ })
7747
+ ) ).sort();
7748
+
7749
+ if ( $.inArray( o.selected, o.disabled ) != -1 ) {
7750
+ o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 );
7751
+ }
7752
+
7753
+ // highlight selected tab
7754
+ this.panels.addClass( "ui-tabs-hide" );
7755
+ this.lis.removeClass( "ui-tabs-selected ui-state-active" );
7756
+ // check for length avoids error when initializing empty list
7757
+ if ( o.selected >= 0 && this.anchors.length ) {
7758
+ self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" );
7759
+ this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" );
7760
+
7761
+ // seems to be expected behavior that the show callback is fired
7762
+ self.element.queue( "tabs", function() {
7763
+ self._trigger( "show", null,
7764
+ self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) );
7765
+ });
7766
+
7767
+ this.load( o.selected );
7768
+ }
7769
+
7770
+ // clean up to avoid memory leaks in certain versions of IE 6
7771
+ // TODO: namespace this event
7772
+ $( window ).bind( "unload", function() {
7773
+ self.lis.add( self.anchors ).unbind( ".tabs" );
7774
+ self.lis = self.anchors = self.panels = null;
7775
+ });
7776
+ // update selected after add/remove
7777
+ } else {
7778
+ o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
7779
+ }
7780
+
7781
+ // update collapsible
7782
+ // TODO: use .toggleClass()
7783
+ this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" );
7784
+
7785
+ // set or update cookie after init and add/remove respectively
7786
+ if ( o.cookie ) {
7787
+ this._cookie( o.selected, o.cookie );
7788
+ }
7789
+
7790
+ // disable tabs
7791
+ for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) {
7792
+ $( li )[ $.inArray( i, o.disabled ) != -1 &&
7793
+ // TODO: use .toggleClass()
7794
+ !$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" );
7795
+ }
7796
+
7797
+ // reset cache if switching from cached to not cached
7798
+ if ( o.cache === false ) {
7799
+ this.anchors.removeData( "cache.tabs" );
7800
+ }
7801
+
7802
+ // remove all handlers before, tabify may run on existing tabs after add or option change
7803
+ this.lis.add( this.anchors ).unbind( ".tabs" );
7804
+
7805
+ if ( o.event !== "mouseover" ) {
7806
+ var addState = function( state, el ) {
7807
+ if ( el.is( ":not(.ui-state-disabled)" ) ) {
7808
+ el.addClass( "ui-state-" + state );
7809
+ }
7810
+ };
7811
+ var removeState = function( state, el ) {
7812
+ el.removeClass( "ui-state-" + state );
7813
+ };
7814
+ this.lis.bind( "mouseover.tabs" , function() {
7815
+ addState( "hover", $( this ) );
7816
+ });
7817
+ this.lis.bind( "mouseout.tabs", function() {
7818
+ removeState( "hover", $( this ) );
7819
+ });
7820
+ this.anchors.bind( "focus.tabs", function() {
7821
+ addState( "focus", $( this ).closest( "li" ) );
7822
+ });
7823
+ this.anchors.bind( "blur.tabs", function() {
7824
+ removeState( "focus", $( this ).closest( "li" ) );
7825
+ });
7826
+ }
7827
+
7828
+ // set up animations
7829
+ var hideFx, showFx;
7830
+ if ( o.fx ) {
7831
+ if ( $.isArray( o.fx ) ) {
7832
+ hideFx = o.fx[ 0 ];
7833
+ showFx = o.fx[ 1 ];
7834
+ } else {
7835
+ hideFx = showFx = o.fx;
7836
+ }
7837
+ }
7838
+
7839
+ // Reset certain styles left over from animation
7840
+ // and prevent IE's ClearType bug...
7841
+ function resetStyle( $el, fx ) {
7842
+ $el.css( "display", "" );
7843
+ if ( !$.support.opacity && fx.opacity ) {
7844
+ $el[ 0 ].style.removeAttribute( "filter" );
7845
+ }
7846
+ }
7847
+
7848
+ // Show a tab...
7849
+ var showTab = showFx
7850
+ ? function( clicked, $show ) {
7851
+ $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
7852
+ $show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way
7853
+ .animate( showFx, showFx.duration || "normal", function() {
7854
+ resetStyle( $show, showFx );
7855
+ self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
7856
+ });
7857
+ }
7858
+ : function( clicked, $show ) {
7859
+ $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
7860
+ $show.removeClass( "ui-tabs-hide" );
7861
+ self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
7862
+ };
7863
+
7864
+ // Hide a tab, $show is optional...
7865
+ var hideTab = hideFx
7866
+ ? function( clicked, $hide ) {
7867
+ $hide.animate( hideFx, hideFx.duration || "normal", function() {
7868
+ self.lis.removeClass( "ui-tabs-selected ui-state-active" );
7869
+ $hide.addClass( "ui-tabs-hide" );
7870
+ resetStyle( $hide, hideFx );
7871
+ self.element.dequeue( "tabs" );
7872
+ });
7873
+ }
7874
+ : function( clicked, $hide, $show ) {
7875
+ self.lis.removeClass( "ui-tabs-selected ui-state-active" );
7876
+ $hide.addClass( "ui-tabs-hide" );
7877
+ self.element.dequeue( "tabs" );
7878
+ };
7879
+
7880
+ // attach tab event handler, unbind to avoid duplicates from former tabifying...
7881
+ this.anchors.bind( o.event + ".tabs", function() {
7882
+ var el = this,
7883
+ $li = $(el).closest( "li" ),
7884
+ $hide = self.panels.filter( ":not(.ui-tabs-hide)" ),
7885
+ $show = self.element.find( self._sanitizeSelector( el.hash ) );
7886
+
7887
+ // If tab is already selected and not collapsible or tab disabled or
7888
+ // or is already loading or click callback returns false stop here.
7889
+ // Check if click handler returns false last so that it is not executed
7890
+ // for a disabled or loading tab!
7891
+ if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) ||
7892
+ $li.hasClass( "ui-state-disabled" ) ||
7893
+ $li.hasClass( "ui-state-processing" ) ||
7894
+ self.panels.filter( ":animated" ).length ||
7895
+ self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) {
7896
+ this.blur();
7897
+ return false;
7898
+ }
7899
+
7900
+ o.selected = self.anchors.index( this );
7901
+
7902
+ self.abort();
7903
+
7904
+ // if tab may be closed
7905
+ if ( o.collapsible ) {
7906
+ if ( $li.hasClass( "ui-tabs-selected" ) ) {
7907
+ o.selected = -1;
7908
+
7909
+ if ( o.cookie ) {
7910
+ self._cookie( o.selected, o.cookie );
7911
+ }
7912
+
7913
+ self.element.queue( "tabs", function() {
7914
+ hideTab( el, $hide );
7915
+ }).dequeue( "tabs" );
7916
+
7917
+ this.blur();
7918
+ return false;
7919
+ } else if ( !$hide.length ) {
7920
+ if ( o.cookie ) {
7921
+ self._cookie( o.selected, o.cookie );
7922
+ }
7923
+
7924
+ self.element.queue( "tabs", function() {
7925
+ showTab( el, $show );
7926
+ });
7927
+
7928
+ // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171
7929
+ self.load( self.anchors.index( this ) );
7930
+
7931
+ this.blur();
7932
+ return false;
7933
+ }
7934
+ }
7935
+
7936
+ if ( o.cookie ) {
7937
+ self._cookie( o.selected, o.cookie );
7938
+ }
7939
+
7940
+ // show new tab
7941
+ if ( $show.length ) {
7942
+ if ( $hide.length ) {
7943
+ self.element.queue( "tabs", function() {
7944
+ hideTab( el, $hide );
7945
+ });
7946
+ }
7947
+ self.element.queue( "tabs", function() {
7948
+ showTab( el, $show );
7949
+ });
7950
+
7951
+ self.load( self.anchors.index( this ) );
7952
+ } else {
7953
+ throw "jQuery UI Tabs: Mismatching fragment identifier.";
7954
+ }
7955
+
7956
+ // Prevent IE from keeping other link focussed when using the back button
7957
+ // and remove dotted border from clicked link. This is controlled via CSS
7958
+ // in modern browsers; blur() removes focus from address bar in Firefox
7959
+ // which can become a usability and annoying problem with tabs('rotate').
7960
+ if ( $.browser.msie ) {
7961
+ this.blur();
7962
+ }
7963
+ });
7964
+
7965
+ // disable click in any case
7966
+ this.anchors.bind( "click.tabs", function(){
7967
+ return false;
7968
+ });
7969
+ },
7970
+
7971
+ _getIndex: function( index ) {
7972
+ // meta-function to give users option to provide a href string instead of a numerical index.
7973
+ // also sanitizes numerical indexes to valid values.
7974
+ if ( typeof index == "string" ) {
7975
+ index = this.anchors.index( this.anchors.filter( "[href$=" + index + "]" ) );
7976
+ }
7977
+
7978
+ return index;
7979
+ },
7980
+
7981
+ destroy: function() {
7982
+ var o = this.options;
7983
+
7984
+ this.abort();
7985
+
7986
+ this.element
7987
+ .unbind( ".tabs" )
7988
+ .removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" )
7989
+ .removeData( "tabs" );
7990
+
7991
+ this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
7992
+
7993
+ this.anchors.each(function() {
7994
+ var href = $.data( this, "href.tabs" );
7995
+ if ( href ) {
7996
+ this.href = href;
7997
+ }
7998
+ var $this = $( this ).unbind( ".tabs" );
7999
+ $.each( [ "href", "load", "cache" ], function( i, prefix ) {
8000
+ $this.removeData( prefix + ".tabs" );
8001
+ });
8002
+ });
8003
+
8004
+ this.lis.unbind( ".tabs" ).add( this.panels ).each(function() {
8005
+ if ( $.data( this, "destroy.tabs" ) ) {
8006
+ $( this ).remove();
8007
+ } else {
8008
+ $( this ).removeClass([
8009
+ "ui-state-default",
8010
+ "ui-corner-top",
8011
+ "ui-tabs-selected",
8012
+ "ui-state-active",
8013
+ "ui-state-hover",
8014
+ "ui-state-focus",
8015
+ "ui-state-disabled",
8016
+ "ui-tabs-panel",
8017
+ "ui-widget-content",
8018
+ "ui-corner-bottom",
8019
+ "ui-tabs-hide"
8020
+ ].join( " " ) );
8021
+ }
8022
+ });
8023
+
8024
+ if ( o.cookie ) {
8025
+ this._cookie( null, o.cookie );
8026
+ }
8027
+
8028
+ return this;
8029
+ },
8030
+
8031
+ add: function( url, label, index ) {
8032
+ if ( index === undefined ) {
8033
+ index = this.anchors.length;
8034
+ }
8035
+
8036
+ var self = this,
8037
+ o = this.options,
8038
+ $li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ),
8039
+ id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] );
8040
+
8041
+ $li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true );
8042
+
8043
+ // try to find an existing element before creating a new one
8044
+ var $panel = self.element.find( "#" + id );
8045
+ if ( !$panel.length ) {
8046
+ $panel = $( o.panelTemplate )
8047
+ .attr( "id", id )
8048
+ .data( "destroy.tabs", true );
8049
+ }
8050
+ $panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" );
8051
+
8052
+ if ( index >= this.lis.length ) {
8053
+ $li.appendTo( this.list );
8054
+ $panel.appendTo( this.list[ 0 ].parentNode );
8055
+ } else {
8056
+ $li.insertBefore( this.lis[ index ] );
8057
+ $panel.insertBefore( this.panels[ index ] );
8058
+ }
8059
+
8060
+ o.disabled = $.map( o.disabled, function( n, i ) {
8061
+ return n >= index ? ++n : n;
8062
+ });
8063
+
8064
+ this._tabify();
8065
+
8066
+ if ( this.anchors.length == 1 ) {
8067
+ o.selected = 0;
8068
+ $li.addClass( "ui-tabs-selected ui-state-active" );
8069
+ $panel.removeClass( "ui-tabs-hide" );
8070
+ this.element.queue( "tabs", function() {
8071
+ self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) );
8072
+ });
8073
+
8074
+ this.load( 0 );
8075
+ }
8076
+
8077
+ this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
8078
+ return this;
8079
+ },
8080
+
8081
+ remove: function( index ) {
8082
+ index = this._getIndex( index );
8083
+ var o = this.options,
8084
+ $li = this.lis.eq( index ).remove(),
8085
+ $panel = this.panels.eq( index ).remove();
8086
+
8087
+ // If selected tab was removed focus tab to the right or
8088
+ // in case the last tab was removed the tab to the left.
8089
+ if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) {
8090
+ this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
8091
+ }
8092
+
8093
+ o.disabled = $.map(
8094
+ $.grep( o.disabled, function(n, i) {
8095
+ return n != index;
8096
+ }),
8097
+ function( n, i ) {
8098
+ return n >= index ? --n : n;
8099
+ });
8100
+
8101
+ this._tabify();
8102
+
8103
+ this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) );
8104
+ return this;
8105
+ },
8106
+
8107
+ enable: function( index ) {
8108
+ index = this._getIndex( index );
8109
+ var o = this.options;
8110
+ if ( $.inArray( index, o.disabled ) == -1 ) {
8111
+ return;
8112
+ }
8113
+
8114
+ this.lis.eq( index ).removeClass( "ui-state-disabled" );
8115
+ o.disabled = $.grep( o.disabled, function( n, i ) {
8116
+ return n != index;
8117
+ });
8118
+
8119
+ this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
8120
+ return this;
8121
+ },
8122
+
8123
+ disable: function( index ) {
8124
+ index = this._getIndex( index );
8125
+ var self = this, o = this.options;
8126
+ // cannot disable already selected tab
8127
+ if ( index != o.selected ) {
8128
+ this.lis.eq( index ).addClass( "ui-state-disabled" );
8129
+
8130
+ o.disabled.push( index );
8131
+ o.disabled.sort();
8132
+
8133
+ this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
8134
+ }
8135
+
8136
+ return this;
8137
+ },
8138
+
8139
+ select: function( index ) {
8140
+ index = this._getIndex( index );
8141
+ if ( index == -1 ) {
8142
+ if ( this.options.collapsible && this.options.selected != -1 ) {
8143
+ index = this.options.selected;
8144
+ } else {
8145
+ return this;
8146
+ }
8147
+ }
8148
+ this.anchors.eq( index ).trigger( this.options.event + ".tabs" );
8149
+ return this;
8150
+ },
8151
+
8152
+ load: function( index ) {
8153
+ index = this._getIndex( index );
8154
+ var self = this,
8155
+ o = this.options,
8156
+ a = this.anchors.eq( index )[ 0 ],
8157
+ url = $.data( a, "load.tabs" );
8158
+
8159
+ this.abort();
8160
+
8161
+ // not remote or from cache
8162
+ if ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) {
8163
+ this.element.dequeue( "tabs" );
8164
+ return;
8165
+ }
8166
+
8167
+ // load remote from here on
8168
+ this.lis.eq( index ).addClass( "ui-state-processing" );
8169
+
8170
+ if ( o.spinner ) {
8171
+ var span = $( "span", a );
8172
+ span.data( "label.tabs", span.html() ).html( o.spinner );
8173
+ }
8174
+
8175
+ this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, {
8176
+ url: url,
8177
+ success: function( r, s ) {
8178
+ self.element.find( self._sanitizeSelector( a.hash ) ).html( r );
8179
+
8180
+ // take care of tab labels
8181
+ self._cleanup();
8182
+
8183
+ if ( o.cache ) {
8184
+ $.data( a, "cache.tabs", true );
8185
+ }
8186
+
8187
+ self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
8188
+ try {
8189
+ o.ajaxOptions.success( r, s );
8190
+ }
8191
+ catch ( e ) {}
8192
+ },
8193
+ error: function( xhr, s, e ) {
8194
+ // take care of tab labels
8195
+ self._cleanup();
8196
+
8197
+ self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
8198
+ try {
8199
+ // Passing index avoid a race condition when this method is
8200
+ // called after the user has selected another tab.
8201
+ // Pass the anchor that initiated this request allows
8202
+ // loadError to manipulate the tab content panel via $(a.hash)
8203
+ o.ajaxOptions.error( xhr, s, index, a );
8204
+ }
8205
+ catch ( e ) {}
8206
+ }
8207
+ } ) );
8208
+
8209
+ // last, so that load event is fired before show...
8210
+ self.element.dequeue( "tabs" );
8211
+
8212
+ return this;
8213
+ },
8214
+
8215
+ abort: function() {
8216
+ // stop possibly running animations
8217
+ this.element.queue( [] );
8218
+ this.panels.stop( false, true );
8219
+
8220
+ // "tabs" queue must not contain more than two elements,
8221
+ // which are the callbacks for the latest clicked tab...
8222
+ this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) );
8223
+
8224
+ // terminate pending requests from other tabs
8225
+ if ( this.xhr ) {
8226
+ this.xhr.abort();
8227
+ delete this.xhr;
8228
+ }
8229
+
8230
+ // take care of tab labels
8231
+ this._cleanup();
8232
+ return this;
8233
+ },
8234
+
8235
+ url: function( index, url ) {
8236
+ this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url );
8237
+ return this;
8238
+ },
8239
+
8240
+ length: function() {
8241
+ return this.anchors.length;
8242
+ }
8243
+ });
8244
+
8245
+ $.extend( $.ui.tabs, {
8246
+ version: "1.8.17"
8247
+ });
8248
+
8249
+ /*
8250
+ * Tabs Extensions
8251
+ */
8252
+
8253
+ /*
8254
+ * Rotate
8255
+ */
8256
+ $.extend( $.ui.tabs.prototype, {
8257
+ rotation: null,
8258
+ rotate: function( ms, continuing ) {
8259
+ var self = this,
8260
+ o = this.options;
8261
+
8262
+ var rotate = self._rotate || ( self._rotate = function( e ) {
8263
+ clearTimeout( self.rotation );
8264
+ self.rotation = setTimeout(function() {
8265
+ var t = o.selected;
8266
+ self.select( ++t < self.anchors.length ? t : 0 );
8267
+ }, ms );
8268
+
8269
+ if ( e ) {
8270
+ e.stopPropagation();
8271
+ }
8272
+ });
8273
+
8274
+ var stop = self._unrotate || ( self._unrotate = !continuing
8275
+ ? function(e) {
8276
+ if (e.clientX) { // in case of a true click
8277
+ self.rotate(null);
8278
+ }
8279
+ }
8280
+ : function( e ) {
8281
+ t = o.selected;
8282
+ rotate();
8283
+ });
8284
+
8285
+ // start rotation
8286
+ if ( ms ) {
8287
+ this.element.bind( "tabsshow", rotate );
8288
+ this.anchors.bind( o.event + ".tabs", stop );
8289
+ rotate();
8290
+ // stop rotation
8291
+ } else {
8292
+ clearTimeout( self.rotation );
8293
+ this.element.unbind( "tabsshow", rotate );
8294
+ this.anchors.unbind( o.event + ".tabs", stop );
8295
+ delete this._rotate;
8296
+ delete this._unrotate;
8297
+ }
8298
+
8299
+ return this;
8300
+ }
8301
+ });
8302
+
8303
+ })( jQuery );
8304
+ /*
8305
+ * jQuery UI Datepicker 1.8.17
8306
+ *
8307
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
8308
+ * Dual licensed under the MIT or GPL Version 2 licenses.
8309
+ * http://jquery.org/license
8310
+ *
8311
+ * http://docs.jquery.com/UI/Datepicker
8312
+ *
8313
+ * Depends:
8314
+ * jquery.ui.core.js
8315
+ */
8316
+ (function( $, undefined ) {
8317
+
8318
+ $.extend($.ui, { datepicker: { version: "1.8.17" } });
8319
+
8320
+ var PROP_NAME = 'datepicker';
8321
+ var dpuuid = new Date().getTime();
8322
+ var instActive;
8323
+
8324
+ /* Date picker manager.
8325
+ Use the singleton instance of this class, $.datepicker, to interact with the date picker.
8326
+ Settings for (groups of) date pickers are maintained in an instance object,
8327
+ allowing multiple different settings on the same page. */
8328
+
8329
+ function Datepicker() {
8330
+ this.debug = false; // Change this to true to start debugging
8331
+ this._curInst = null; // The current instance in use
8332
+ this._keyEvent = false; // If the last event was a key event
8333
+ this._disabledInputs = []; // List of date picker inputs that have been disabled
8334
+ this._datepickerShowing = false; // True if the popup picker is showing , false if not
8335
+ this._inDialog = false; // True if showing within a "dialog", false if not
8336
+ this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
8337
+ this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
8338
+ this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
8339
+ this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
8340
+ this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
8341
+ this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
8342
+ this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
8343
+ this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
8344
+ this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
8345
+ this.regional = []; // Available regional settings, indexed by language code
8346
+ this.regional[''] = { // Default regional settings
8347
+ closeText: 'Done', // Display text for close link
8348
+ prevText: 'Prev', // Display text for previous month link
8349
+ nextText: 'Next', // Display text for next month link
8350
+ currentText: 'Today', // Display text for current month link
8351
+ monthNames: ['January','February','March','April','May','June',
8352
+ 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
8353
+ monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
8354
+ dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
8355
+ dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
8356
+ dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
8357
+ weekHeader: 'Wk', // Column header for week of the year
8358
+ dateFormat: 'mm/dd/yy', // See format options on parseDate
8359
+ firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
8360
+ isRTL: false, // True if right-to-left language, false if left-to-right
8361
+ showMonthAfterYear: false, // True if the year select precedes month, false for month then year
8362
+ yearSuffix: '' // Additional text to append to the year in the month headers
8363
+ };
8364
+ this._defaults = { // Global defaults for all the date picker instances
8365
+ showOn: 'focus', // 'focus' for popup on focus,
8366
+ // 'button' for trigger button, or 'both' for either
8367
+ showAnim: 'fadeIn', // Name of jQuery animation for popup
8368
+ showOptions: {}, // Options for enhanced animations
8369
+ defaultDate: null, // Used when field is blank: actual date,
8370
+ // +/-number for offset from today, null for today
8371
+ appendText: '', // Display text following the input box, e.g. showing the format
8372
+ buttonText: '...', // Text for trigger button
8373
+ buttonImage: '', // URL for trigger button image
8374
+ buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
8375
+ hideIfNoPrevNext: false, // True to hide next/previous month links
8376
+ // if not applicable, false to just disable them
8377
+ navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
8378
+ gotoCurrent: false, // True if today link goes back to current selection instead
8379
+ changeMonth: false, // True if month can be selected directly, false if only prev/next
8380
+ changeYear: false, // True if year can be selected directly, false if only prev/next
8381
+ yearRange: 'c-10:c+10', // Range of years to display in drop-down,
8382
+ // either relative to today's year (-nn:+nn), relative to currently displayed year
8383
+ // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
8384
+ showOtherMonths: false, // True to show dates in other months, false to leave blank
8385
+ selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
8386
+ showWeek: false, // True to show week of the year, false to not show it
8387
+ calculateWeek: this.iso8601Week, // How to calculate the week of the year,
8388
+ // takes a Date and returns the number of the week for it
8389
+ shortYearCutoff: '+10', // Short year values < this are in the current century,
8390
+ // > this are in the previous century,
8391
+ // string value starting with '+' for current year + value
8392
+ minDate: null, // The earliest selectable date, or null for no limit
8393
+ maxDate: null, // The latest selectable date, or null for no limit
8394
+ duration: 'fast', // Duration of display/closure
8395
+ beforeShowDay: null, // Function that takes a date and returns an array with
8396
+ // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
8397
+ // [2] = cell title (optional), e.g. $.datepicker.noWeekends
8398
+ beforeShow: null, // Function that takes an input field and
8399
+ // returns a set of custom settings for the date picker
8400
+ onSelect: null, // Define a callback function when a date is selected
8401
+ onChangeMonthYear: null, // Define a callback function when the month or year is changed
8402
+ onClose: null, // Define a callback function when the datepicker is closed
8403
+ numberOfMonths: 1, // Number of months to show at a time
8404
+ showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
8405
+ stepMonths: 1, // Number of months to step back/forward
8406
+ stepBigMonths: 12, // Number of months to step back/forward for the big links
8407
+ altField: '', // Selector for an alternate field to store selected dates into
8408
+ altFormat: '', // The date format to use for the alternate field
8409
+ constrainInput: true, // The input is constrained by the current date format
8410
+ showButtonPanel: false, // True to show button panel, false to not show it
8411
+ autoSize: false, // True to size the input for the date format, false to leave as is
8412
+ disabled: false // The initial disabled state
8413
+ };
8414
+ $.extend(this._defaults, this.regional['']);
8415
+ this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
8416
+ }
8417
+
8418
+ $.extend(Datepicker.prototype, {
8419
+ /* Class name added to elements to indicate already configured with a date picker. */
8420
+ markerClassName: 'hasDatepicker',
8421
+
8422
+ //Keep track of the maximum number of rows displayed (see #7043)
8423
+ maxRows: 4,
8424
+
8425
+ /* Debug logging (if enabled). */
8426
+ log: function () {
8427
+ if (this.debug)
8428
+ console.log.apply('', arguments);
8429
+ },
8430
+
8431
+ // TODO rename to "widget" when switching to widget factory
8432
+ _widgetDatepicker: function() {
8433
+ return this.dpDiv;
8434
+ },
8435
+
8436
+ /* Override the default settings for all instances of the date picker.
8437
+ @param settings object - the new settings to use as defaults (anonymous object)
8438
+ @return the manager object */
8439
+ setDefaults: function(settings) {
8440
+ extendRemove(this._defaults, settings || {});
8441
+ return this;
8442
+ },
8443
+
8444
+ /* Attach the date picker to a jQuery selection.
8445
+ @param target element - the target input field or division or span
8446
+ @param settings object - the new settings to use for this date picker instance (anonymous) */
8447
+ _attachDatepicker: function(target, settings) {
8448
+ // check for settings on the control itself - in namespace 'date:'
8449
+ var inlineSettings = null;
8450
+ for (var attrName in this._defaults) {
8451
+ var attrValue = target.getAttribute('date:' + attrName);
8452
+ if (attrValue) {
8453
+ inlineSettings = inlineSettings || {};
8454
+ try {
8455
+ inlineSettings[attrName] = eval(attrValue);
8456
+ } catch (err) {
8457
+ inlineSettings[attrName] = attrValue;
8458
+ }
8459
+ }
8460
+ }
8461
+ var nodeName = target.nodeName.toLowerCase();
8462
+ var inline = (nodeName == 'div' || nodeName == 'span');
8463
+ if (!target.id) {
8464
+ this.uuid += 1;
8465
+ target.id = 'dp' + this.uuid;
8466
+ }
8467
+ var inst = this._newInst($(target), inline);
8468
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
8469
+ if (nodeName == 'input') {
8470
+ this._connectDatepicker(target, inst);
8471
+ } else if (inline) {
8472
+ this._inlineDatepicker(target, inst);
8473
+ }
8474
+ },
8475
+
8476
+ /* Create a new instance object. */
8477
+ _newInst: function(target, inline) {
8478
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
8479
+ return {id: id, input: target, // associated target
8480
+ selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
8481
+ drawMonth: 0, drawYear: 0, // month being drawn
8482
+ inline: inline, // is datepicker inline or not
8483
+ dpDiv: (!inline ? this.dpDiv : // presentation div
8484
+ bindHover($('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')))};
8485
+ },
8486
+
8487
+ /* Attach the date picker to an input field. */
8488
+ _connectDatepicker: function(target, inst) {
8489
+ var input = $(target);
8490
+ inst.append = $([]);
8491
+ inst.trigger = $([]);
8492
+ if (input.hasClass(this.markerClassName))
8493
+ return;
8494
+ this._attachments(input, inst);
8495
+ input.addClass(this.markerClassName).keydown(this._doKeyDown).
8496
+ keypress(this._doKeyPress).keyup(this._doKeyUp).
8497
+ bind("setData.datepicker", function(event, key, value) {
8498
+ inst.settings[key] = value;
8499
+ }).bind("getData.datepicker", function(event, key) {
8500
+ return this._get(inst, key);
8501
+ });
8502
+ this._autoSize(inst);
8503
+ $.data(target, PROP_NAME, inst);
8504
+ //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
8505
+ if( inst.settings.disabled ) {
8506
+ this._disableDatepicker( target );
8507
+ }
8508
+ },
8509
+
8510
+ /* Make attachments based on settings. */
8511
+ _attachments: function(input, inst) {
8512
+ var appendText = this._get(inst, 'appendText');
8513
+ var isRTL = this._get(inst, 'isRTL');
8514
+ if (inst.append)
8515
+ inst.append.remove();
8516
+ if (appendText) {
8517
+ inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
8518
+ input[isRTL ? 'before' : 'after'](inst.append);
8519
+ }
8520
+ input.unbind('focus', this._showDatepicker);
8521
+ if (inst.trigger)
8522
+ inst.trigger.remove();
8523
+ var showOn = this._get(inst, 'showOn');
8524
+ if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
8525
+ input.focus(this._showDatepicker);
8526
+ if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
8527
+ var buttonText = this._get(inst, 'buttonText');
8528
+ var buttonImage = this._get(inst, 'buttonImage');
8529
+ inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
8530
+ $('<img/>').addClass(this._triggerClass).
8531
+ attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
8532
+ $('<button type="button"></button>').addClass(this._triggerClass).
8533
+ html(buttonImage == '' ? buttonText : $('<img/>').attr(
8534
+ { src:buttonImage, alt:buttonText, title:buttonText })));
8535
+ input[isRTL ? 'before' : 'after'](inst.trigger);
8536
+ inst.trigger.click(function() {
8537
+ if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0])
8538
+ $.datepicker._hideDatepicker();
8539
+ else
8540
+ $.datepicker._showDatepicker(input[0]);
8541
+ return false;
8542
+ });
8543
+ }
8544
+ },
8545
+
8546
+ /* Apply the maximum length for the date format. */
8547
+ _autoSize: function(inst) {
8548
+ if (this._get(inst, 'autoSize') && !inst.inline) {
8549
+ var date = new Date(2009, 12 - 1, 20); // Ensure double digits
8550
+ var dateFormat = this._get(inst, 'dateFormat');
8551
+ if (dateFormat.match(/[DM]/)) {
8552
+ var findMax = function(names) {
8553
+ var max = 0;
8554
+ var maxI = 0;
8555
+ for (var i = 0; i < names.length; i++) {
8556
+ if (names[i].length > max) {
8557
+ max = names[i].length;
8558
+ maxI = i;
8559
+ }
8560
+ }
8561
+ return maxI;
8562
+ };
8563
+ date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
8564
+ 'monthNames' : 'monthNamesShort'))));
8565
+ date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
8566
+ 'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
8567
+ }
8568
+ inst.input.attr('size', this._formatDate(inst, date).length);
8569
+ }
8570
+ },
8571
+
8572
+ /* Attach an inline date picker to a div. */
8573
+ _inlineDatepicker: function(target, inst) {
8574
+ var divSpan = $(target);
8575
+ if (divSpan.hasClass(this.markerClassName))
8576
+ return;
8577
+ divSpan.addClass(this.markerClassName).append(inst.dpDiv).
8578
+ bind("setData.datepicker", function(event, key, value){
8579
+ inst.settings[key] = value;
8580
+ }).bind("getData.datepicker", function(event, key){
8581
+ return this._get(inst, key);
8582
+ });
8583
+ $.data(target, PROP_NAME, inst);
8584
+ this._setDate(inst, this._getDefaultDate(inst), true);
8585
+ this._updateDatepicker(inst);
8586
+ this._updateAlternate(inst);
8587
+ //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
8588
+ if( inst.settings.disabled ) {
8589
+ this._disableDatepicker( target );
8590
+ }
8591
+ // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
8592
+ // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
8593
+ inst.dpDiv.css( "display", "block" );
8594
+ },
8595
+
8596
+ /* Pop-up the date picker in a "dialog" box.
8597
+ @param input element - ignored
8598
+ @param date string or Date - the initial date to display
8599
+ @param onSelect function - the function to call when a date is selected
8600
+ @param settings object - update the dialog date picker instance's settings (anonymous object)
8601
+ @param pos int[2] - coordinates for the dialog's position within the screen or
8602
+ event - with x/y coordinates or
8603
+ leave empty for default (screen centre)
8604
+ @return the manager object */
8605
+ _dialogDatepicker: function(input, date, onSelect, settings, pos) {
8606
+ var inst = this._dialogInst; // internal instance
8607
+ if (!inst) {
8608
+ this.uuid += 1;
8609
+ var id = 'dp' + this.uuid;
8610
+ this._dialogInput = $('<input type="text" id="' + id +
8611
+ '" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');
8612
+ this._dialogInput.keydown(this._doKeyDown);
8613
+ $('body').append(this._dialogInput);
8614
+ inst = this._dialogInst = this._newInst(this._dialogInput, false);
8615
+ inst.settings = {};
8616
+ $.data(this._dialogInput[0], PROP_NAME, inst);
8617
+ }
8618
+ extendRemove(inst.settings, settings || {});
8619
+ date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
8620
+ this._dialogInput.val(date);
8621
+
8622
+ this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
8623
+ if (!this._pos) {
8624
+ var browserWidth = document.documentElement.clientWidth;
8625
+ var browserHeight = document.documentElement.clientHeight;
8626
+ var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
8627
+ var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
8628
+ this._pos = // should use actual width/height below
8629
+ [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
8630
+ }
8631
+
8632
+ // move input on screen for focus, but hidden behind dialog
8633
+ this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
8634
+ inst.settings.onSelect = onSelect;
8635
+ this._inDialog = true;
8636
+ this.dpDiv.addClass(this._dialogClass);
8637
+ this._showDatepicker(this._dialogInput[0]);
8638
+ if ($.blockUI)
8639
+ $.blockUI(this.dpDiv);
8640
+ $.data(this._dialogInput[0], PROP_NAME, inst);
8641
+ return this;
8642
+ },
8643
+
8644
+ /* Detach a datepicker from its control.
8645
+ @param target element - the target input field or division or span */
8646
+ _destroyDatepicker: function(target) {
8647
+ var $target = $(target);
8648
+ var inst = $.data(target, PROP_NAME);
8649
+ if (!$target.hasClass(this.markerClassName)) {
8650
+ return;
8651
+ }
8652
+ var nodeName = target.nodeName.toLowerCase();
8653
+ $.removeData(target, PROP_NAME);
8654
+ if (nodeName == 'input') {
8655
+ inst.append.remove();
8656
+ inst.trigger.remove();
8657
+ $target.removeClass(this.markerClassName).
8658
+ unbind('focus', this._showDatepicker).
8659
+ unbind('keydown', this._doKeyDown).
8660
+ unbind('keypress', this._doKeyPress).
8661
+ unbind('keyup', this._doKeyUp);
8662
+ } else if (nodeName == 'div' || nodeName == 'span')
8663
+ $target.removeClass(this.markerClassName).empty();
8664
+ },
8665
+
8666
+ /* Enable the date picker to a jQuery selection.
8667
+ @param target element - the target input field or division or span */
8668
+ _enableDatepicker: function(target) {
8669
+ var $target = $(target);
8670
+ var inst = $.data(target, PROP_NAME);
8671
+ if (!$target.hasClass(this.markerClassName)) {
8672
+ return;
8673
+ }
8674
+ var nodeName = target.nodeName.toLowerCase();
8675
+ if (nodeName == 'input') {
8676
+ target.disabled = false;
8677
+ inst.trigger.filter('button').
8678
+ each(function() { this.disabled = false; }).end().
8679
+ filter('img').css({opacity: '1.0', cursor: ''});
8680
+ }
8681
+ else if (nodeName == 'div' || nodeName == 'span') {
8682
+ var inline = $target.children('.' + this._inlineClass);
8683
+ inline.children().removeClass('ui-state-disabled');
8684
+ inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
8685
+ removeAttr("disabled");
8686
+ }
8687
+ this._disabledInputs = $.map(this._disabledInputs,
8688
+ function(value) { return (value == target ? null : value); }); // delete entry
8689
+ },
8690
+
8691
+ /* Disable the date picker to a jQuery selection.
8692
+ @param target element - the target input field or division or span */
8693
+ _disableDatepicker: function(target) {
8694
+ var $target = $(target);
8695
+ var inst = $.data(target, PROP_NAME);
8696
+ if (!$target.hasClass(this.markerClassName)) {
8697
+ return;
8698
+ }
8699
+ var nodeName = target.nodeName.toLowerCase();
8700
+ if (nodeName == 'input') {
8701
+ target.disabled = true;
8702
+ inst.trigger.filter('button').
8703
+ each(function() { this.disabled = true; }).end().
8704
+ filter('img').css({opacity: '0.5', cursor: 'default'});
8705
+ }
8706
+ else if (nodeName == 'div' || nodeName == 'span') {
8707
+ var inline = $target.children('.' + this._inlineClass);
8708
+ inline.children().addClass('ui-state-disabled');
8709
+ inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
8710
+ attr("disabled", "disabled");
8711
+ }
8712
+ this._disabledInputs = $.map(this._disabledInputs,
8713
+ function(value) { return (value == target ? null : value); }); // delete entry
8714
+ this._disabledInputs[this._disabledInputs.length] = target;
8715
+ },
8716
+
8717
+ /* Is the first field in a jQuery collection disabled as a datepicker?
8718
+ @param target element - the target input field or division or span
8719
+ @return boolean - true if disabled, false if enabled */
8720
+ _isDisabledDatepicker: function(target) {
8721
+ if (!target) {
8722
+ return false;
8723
+ }
8724
+ for (var i = 0; i < this._disabledInputs.length; i++) {
8725
+ if (this._disabledInputs[i] == target)
8726
+ return true;
8727
+ }
8728
+ return false;
8729
+ },
8730
+
8731
+ /* Retrieve the instance data for the target control.
8732
+ @param target element - the target input field or division or span
8733
+ @return object - the associated instance data
8734
+ @throws error if a jQuery problem getting data */
8735
+ _getInst: function(target) {
8736
+ try {
8737
+ return $.data(target, PROP_NAME);
8738
+ }
8739
+ catch (err) {
8740
+ throw 'Missing instance data for this datepicker';
8741
+ }
8742
+ },
8743
+
8744
+ /* Update or retrieve the settings for a date picker attached to an input field or division.
8745
+ @param target element - the target input field or division or span
8746
+ @param name object - the new settings to update or
8747
+ string - the name of the setting to change or retrieve,
8748
+ when retrieving also 'all' for all instance settings or
8749
+ 'defaults' for all global defaults
8750
+ @param value any - the new value for the setting
8751
+ (omit if above is an object or to retrieve a value) */
8752
+ _optionDatepicker: function(target, name, value) {
8753
+ var inst = this._getInst(target);
8754
+ if (arguments.length == 2 && typeof name == 'string') {
8755
+ return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
8756
+ (inst ? (name == 'all' ? $.extend({}, inst.settings) :
8757
+ this._get(inst, name)) : null));
8758
+ }
8759
+ var settings = name || {};
8760
+ if (typeof name == 'string') {
8761
+ settings = {};
8762
+ settings[name] = value;
8763
+ }
8764
+ if (inst) {
8765
+ if (this._curInst == inst) {
8766
+ this._hideDatepicker();
8767
+ }
8768
+ var date = this._getDateDatepicker(target, true);
8769
+ var minDate = this._getMinMaxDate(inst, 'min');
8770
+ var maxDate = this._getMinMaxDate(inst, 'max');
8771
+ extendRemove(inst.settings, settings);
8772
+ // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
8773
+ if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined)
8774
+ inst.settings.minDate = this._formatDate(inst, minDate);
8775
+ if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined)
8776
+ inst.settings.maxDate = this._formatDate(inst, maxDate);
8777
+ this._attachments($(target), inst);
8778
+ this._autoSize(inst);
8779
+ this._setDate(inst, date);
8780
+ this._updateAlternate(inst);
8781
+ this._updateDatepicker(inst);
8782
+ }
8783
+ },
8784
+
8785
+ // change method deprecated
8786
+ _changeDatepicker: function(target, name, value) {
8787
+ this._optionDatepicker(target, name, value);
8788
+ },
8789
+
8790
+ /* Redraw the date picker attached to an input field or division.
8791
+ @param target element - the target input field or division or span */
8792
+ _refreshDatepicker: function(target) {
8793
+ var inst = this._getInst(target);
8794
+ if (inst) {
8795
+ this._updateDatepicker(inst);
8796
+ }
8797
+ },
8798
+
8799
+ /* Set the dates for a jQuery selection.
8800
+ @param target element - the target input field or division or span
8801
+ @param date Date - the new date */
8802
+ _setDateDatepicker: function(target, date) {
8803
+ var inst = this._getInst(target);
8804
+ if (inst) {
8805
+ this._setDate(inst, date);
8806
+ this._updateDatepicker(inst);
8807
+ this._updateAlternate(inst);
8808
+ }
8809
+ },
8810
+
8811
+ /* Get the date(s) for the first entry in a jQuery selection.
8812
+ @param target element - the target input field or division or span
8813
+ @param noDefault boolean - true if no default date is to be used
8814
+ @return Date - the current date */
8815
+ _getDateDatepicker: function(target, noDefault) {
8816
+ var inst = this._getInst(target);
8817
+ if (inst && !inst.inline)
8818
+ this._setDateFromField(inst, noDefault);
8819
+ return (inst ? this._getDate(inst) : null);
8820
+ },
8821
+
8822
+ /* Handle keystrokes. */
8823
+ _doKeyDown: function(event) {
8824
+ var inst = $.datepicker._getInst(event.target);
8825
+ var handled = true;
8826
+ var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
8827
+ inst._keyEvent = true;
8828
+ if ($.datepicker._datepickerShowing)
8829
+ switch (event.keyCode) {
8830
+ case 9: $.datepicker._hideDatepicker();
8831
+ handled = false;
8832
+ break; // hide on tab out
8833
+ case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
8834
+ $.datepicker._currentClass + ')', inst.dpDiv);
8835
+ if (sel[0])
8836
+ $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
8837
+ var onSelect = $.datepicker._get(inst, 'onSelect');
8838
+ if (onSelect) {
8839
+ var dateStr = $.datepicker._formatDate(inst);
8840
+
8841
+ // trigger custom callback
8842
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
8843
+ }
8844
+ else
8845
+ $.datepicker._hideDatepicker();
8846
+ return false; // don't submit the form
8847
+ break; // select the value on enter
8848
+ case 27: $.datepicker._hideDatepicker();
8849
+ break; // hide on escape
8850
+ case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8851
+ -$.datepicker._get(inst, 'stepBigMonths') :
8852
+ -$.datepicker._get(inst, 'stepMonths')), 'M');
8853
+ break; // previous month/year on page up/+ ctrl
8854
+ case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8855
+ +$.datepicker._get(inst, 'stepBigMonths') :
8856
+ +$.datepicker._get(inst, 'stepMonths')), 'M');
8857
+ break; // next month/year on page down/+ ctrl
8858
+ case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
8859
+ handled = event.ctrlKey || event.metaKey;
8860
+ break; // clear on ctrl or command +end
8861
+ case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
8862
+ handled = event.ctrlKey || event.metaKey;
8863
+ break; // current on ctrl or command +home
8864
+ case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
8865
+ handled = event.ctrlKey || event.metaKey;
8866
+ // -1 day on ctrl or command +left
8867
+ if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8868
+ -$.datepicker._get(inst, 'stepBigMonths') :
8869
+ -$.datepicker._get(inst, 'stepMonths')), 'M');
8870
+ // next month/year on alt +left on Mac
8871
+ break;
8872
+ case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
8873
+ handled = event.ctrlKey || event.metaKey;
8874
+ break; // -1 week on ctrl or command +up
8875
+ case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
8876
+ handled = event.ctrlKey || event.metaKey;
8877
+ // +1 day on ctrl or command +right
8878
+ if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8879
+ +$.datepicker._get(inst, 'stepBigMonths') :
8880
+ +$.datepicker._get(inst, 'stepMonths')), 'M');
8881
+ // next month/year on alt +right
8882
+ break;
8883
+ case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
8884
+ handled = event.ctrlKey || event.metaKey;
8885
+ break; // +1 week on ctrl or command +down
8886
+ default: handled = false;
8887
+ }
8888
+ else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
8889
+ $.datepicker._showDatepicker(this);
8890
+ else {
8891
+ handled = false;
8892
+ }
8893
+ if (handled) {
8894
+ event.preventDefault();
8895
+ event.stopPropagation();
8896
+ }
8897
+ },
8898
+
8899
+ /* Filter entered characters - based on date format. */
8900
+ _doKeyPress: function(event) {
8901
+ var inst = $.datepicker._getInst(event.target);
8902
+ if ($.datepicker._get(inst, 'constrainInput')) {
8903
+ var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
8904
+ var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
8905
+ return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
8906
+ }
8907
+ },
8908
+
8909
+ /* Synchronise manual entry and field/alternate field. */
8910
+ _doKeyUp: function(event) {
8911
+ var inst = $.datepicker._getInst(event.target);
8912
+ if (inst.input.val() != inst.lastVal) {
8913
+ try {
8914
+ var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
8915
+ (inst.input ? inst.input.val() : null),
8916
+ $.datepicker._getFormatConfig(inst));
8917
+ if (date) { // only if valid
8918
+ $.datepicker._setDateFromField(inst);
8919
+ $.datepicker._updateAlternate(inst);
8920
+ $.datepicker._updateDatepicker(inst);
8921
+ }
8922
+ }
8923
+ catch (event) {
8924
+ $.datepicker.log(event);
8925
+ }
8926
+ }
8927
+ return true;
8928
+ },
8929
+
8930
+ /* Pop-up the date picker for a given input field.
8931
+ If false returned from beforeShow event handler do not show.
8932
+ @param input element - the input field attached to the date picker or
8933
+ event - if triggered by focus */
8934
+ _showDatepicker: function(input) {
8935
+ input = input.target || input;
8936
+ if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
8937
+ input = $('input', input.parentNode)[0];
8938
+ if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
8939
+ return;
8940
+ var inst = $.datepicker._getInst(input);
8941
+ if ($.datepicker._curInst && $.datepicker._curInst != inst) {
8942
+ $.datepicker._curInst.dpDiv.stop(true, true);
8943
+ if ( inst && $.datepicker._datepickerShowing ) {
8944
+ $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
8945
+ }
8946
+ }
8947
+ var beforeShow = $.datepicker._get(inst, 'beforeShow');
8948
+ var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
8949
+ if(beforeShowSettings === false){
8950
+ //false
8951
+ return;
8952
+ }
8953
+ extendRemove(inst.settings, beforeShowSettings);
8954
+ inst.lastVal = null;
8955
+ $.datepicker._lastInput = input;
8956
+ $.datepicker._setDateFromField(inst);
8957
+ if ($.datepicker._inDialog) // hide cursor
8958
+ input.value = '';
8959
+ if (!$.datepicker._pos) { // position below input
8960
+ $.datepicker._pos = $.datepicker._findPos(input);
8961
+ $.datepicker._pos[1] += input.offsetHeight; // add the height
8962
+ }
8963
+ var isFixed = false;
8964
+ $(input).parents().each(function() {
8965
+ isFixed |= $(this).css('position') == 'fixed';
8966
+ return !isFixed;
8967
+ });
8968
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
8969
+ $.datepicker._pos[0] -= document.documentElement.scrollLeft;
8970
+ $.datepicker._pos[1] -= document.documentElement.scrollTop;
8971
+ }
8972
+ var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
8973
+ $.datepicker._pos = null;
8974
+ //to avoid flashes on Firefox
8975
+ inst.dpDiv.empty();
8976
+ // determine sizing offscreen
8977
+ inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
8978
+ $.datepicker._updateDatepicker(inst);
8979
+ // fix width for dynamic number of date pickers
8980
+ // and adjust position before showing
8981
+ offset = $.datepicker._checkOffset(inst, offset, isFixed);
8982
+ inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
8983
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
8984
+ left: offset.left + 'px', top: offset.top + 'px'});
8985
+ if (!inst.inline) {
8986
+ var showAnim = $.datepicker._get(inst, 'showAnim');
8987
+ var duration = $.datepicker._get(inst, 'duration');
8988
+ var postProcess = function() {
8989
+ var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
8990
+ if( !! cover.length ){
8991
+ var borders = $.datepicker._getBorders(inst.dpDiv);
8992
+ cover.css({left: -borders[0], top: -borders[1],
8993
+ width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
8994
+ }
8995
+ };
8996
+ inst.dpDiv.zIndex($(input).zIndex()+1);
8997
+ $.datepicker._datepickerShowing = true;
8998
+ if ($.effects && $.effects[showAnim])
8999
+ inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
9000
+ else
9001
+ inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
9002
+ if (!showAnim || !duration)
9003
+ postProcess();
9004
+ if (inst.input.is(':visible') && !inst.input.is(':disabled'))
9005
+ inst.input.focus();
9006
+ $.datepicker._curInst = inst;
9007
+ }
9008
+ },
9009
+
9010
+ /* Generate the date picker content. */
9011
+ _updateDatepicker: function(inst) {
9012
+ var self = this;
9013
+ self.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
9014
+ var borders = $.datepicker._getBorders(inst.dpDiv);
9015
+ instActive = inst; // for delegate hover events
9016
+ inst.dpDiv.empty().append(this._generateHTML(inst));
9017
+ var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
9018
+ if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
9019
+ cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
9020
+ }
9021
+ inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
9022
+ var numMonths = this._getNumberOfMonths(inst);
9023
+ var cols = numMonths[1];
9024
+ var width = 17;
9025
+ inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
9026
+ if (cols > 1)
9027
+ inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
9028
+ inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
9029
+ 'Class']('ui-datepicker-multi');
9030
+ inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
9031
+ 'Class']('ui-datepicker-rtl');
9032
+ if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
9033
+ // #6694 - don't focus the input if it's already focused
9034
+ // this breaks the change event in IE
9035
+ inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
9036
+ inst.input.focus();
9037
+ // deffered render of the years select (to avoid flashes on Firefox)
9038
+ if( inst.yearshtml ){
9039
+ var origyearshtml = inst.yearshtml;
9040
+ setTimeout(function(){
9041
+ //assure that inst.yearshtml didn't change.
9042
+ if( origyearshtml === inst.yearshtml && inst.yearshtml ){
9043
+ inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
9044
+ }
9045
+ origyearshtml = inst.yearshtml = null;
9046
+ }, 0);
9047
+ }
9048
+ },
9049
+
9050
+ /* Retrieve the size of left and top borders for an element.
9051
+ @param elem (jQuery object) the element of interest
9052
+ @return (number[2]) the left and top borders */
9053
+ _getBorders: function(elem) {
9054
+ var convert = function(value) {
9055
+ return {thin: 1, medium: 2, thick: 3}[value] || value;
9056
+ };
9057
+ return [parseFloat(convert(elem.css('border-left-width'))),
9058
+ parseFloat(convert(elem.css('border-top-width')))];
9059
+ },
9060
+
9061
+ /* Check positioning to remain on screen. */
9062
+ _checkOffset: function(inst, offset, isFixed) {
9063
+ var dpWidth = inst.dpDiv.outerWidth();
9064
+ var dpHeight = inst.dpDiv.outerHeight();
9065
+ var inputWidth = inst.input ? inst.input.outerWidth() : 0;
9066
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
9067
+ var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
9068
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
9069
+
9070
+ offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
9071
+ offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
9072
+ offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
9073
+
9074
+ // now check if datepicker is showing outside window viewport - move to a better place if so.
9075
+ offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
9076
+ Math.abs(offset.left + dpWidth - viewWidth) : 0);
9077
+ offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
9078
+ Math.abs(dpHeight + inputHeight) : 0);
9079
+
9080
+ return offset;
9081
+ },
9082
+
9083
+ /* Find an object's position on the screen. */
9084
+ _findPos: function(obj) {
9085
+ var inst = this._getInst(obj);
9086
+ var isRTL = this._get(inst, 'isRTL');
9087
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
9088
+ obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
9089
+ }
9090
+ var position = $(obj).offset();
9091
+ return [position.left, position.top];
9092
+ },
9093
+
9094
+ /* Hide the date picker from view.
9095
+ @param input element - the input field attached to the date picker */
9096
+ _hideDatepicker: function(input) {
9097
+ var inst = this._curInst;
9098
+ if (!inst || (input && inst != $.data(input, PROP_NAME)))
9099
+ return;
9100
+ if (this._datepickerShowing) {
9101
+ var showAnim = this._get(inst, 'showAnim');
9102
+ var duration = this._get(inst, 'duration');
9103
+ var self = this;
9104
+ var postProcess = function() {
9105
+ $.datepicker._tidyDialog(inst);
9106
+ self._curInst = null;
9107
+ };
9108
+ if ($.effects && $.effects[showAnim])
9109
+ inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
9110
+ else
9111
+ inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
9112
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
9113
+ if (!showAnim)
9114
+ postProcess();
9115
+ this._datepickerShowing = false;
9116
+ var onClose = this._get(inst, 'onClose');
9117
+ if (onClose)
9118
+ onClose.apply((inst.input ? inst.input[0] : null),
9119
+ [(inst.input ? inst.input.val() : ''), inst]);
9120
+ this._lastInput = null;
9121
+ if (this._inDialog) {
9122
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
9123
+ if ($.blockUI) {
9124
+ $.unblockUI();
9125
+ $('body').append(this.dpDiv);
9126
+ }
9127
+ }
9128
+ this._inDialog = false;
9129
+ }
9130
+ },
9131
+
9132
+ /* Tidy up after a dialog display. */
9133
+ _tidyDialog: function(inst) {
9134
+ inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
9135
+ },
9136
+
9137
+ /* Close date picker if clicked elsewhere. */
9138
+ _checkExternalClick: function(event) {
9139
+ if (!$.datepicker._curInst)
9140
+ return;
9141
+
9142
+ var $target = $(event.target),
9143
+ inst = $.datepicker._getInst($target[0]);
9144
+
9145
+ if ( ( ( $target[0].id != $.datepicker._mainDivId &&
9146
+ $target.parents('#' + $.datepicker._mainDivId).length == 0 &&
9147
+ !$target.hasClass($.datepicker.markerClassName) &&
9148
+ !$target.hasClass($.datepicker._triggerClass) &&
9149
+ $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
9150
+ ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) )
9151
+ $.datepicker._hideDatepicker();
9152
+ },
9153
+
9154
+ /* Adjust one of the date sub-fields. */
9155
+ _adjustDate: function(id, offset, period) {
9156
+ var target = $(id);
9157
+ var inst = this._getInst(target[0]);
9158
+ if (this._isDisabledDatepicker(target[0])) {
9159
+ return;
9160
+ }
9161
+ this._adjustInstDate(inst, offset +
9162
+ (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
9163
+ period);
9164
+ this._updateDatepicker(inst);
9165
+ },
9166
+
9167
+ /* Action for current link. */
9168
+ _gotoToday: function(id) {
9169
+ var target = $(id);
9170
+ var inst = this._getInst(target[0]);
9171
+ if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
9172
+ inst.selectedDay = inst.currentDay;
9173
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth;
9174
+ inst.drawYear = inst.selectedYear = inst.currentYear;
9175
+ }
9176
+ else {
9177
+ var date = new Date();
9178
+ inst.selectedDay = date.getDate();
9179
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
9180
+ inst.drawYear = inst.selectedYear = date.getFullYear();
9181
+ }
9182
+ this._notifyChange(inst);
9183
+ this._adjustDate(target);
9184
+ },
9185
+
9186
+ /* Action for selecting a new month/year. */
9187
+ _selectMonthYear: function(id, select, period) {
9188
+ var target = $(id);
9189
+ var inst = this._getInst(target[0]);
9190
+ inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
9191
+ inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
9192
+ parseInt(select.options[select.selectedIndex].value,10);
9193
+ this._notifyChange(inst);
9194
+ this._adjustDate(target);
9195
+ },
9196
+
9197
+ /* Action for selecting a day. */
9198
+ _selectDay: function(id, month, year, td) {
9199
+ var target = $(id);
9200
+ if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
9201
+ return;
9202
+ }
9203
+ var inst = this._getInst(target[0]);
9204
+ inst.selectedDay = inst.currentDay = $('a', td).html();
9205
+ inst.selectedMonth = inst.currentMonth = month;
9206
+ inst.selectedYear = inst.currentYear = year;
9207
+ this._selectDate(id, this._formatDate(inst,
9208
+ inst.currentDay, inst.currentMonth, inst.currentYear));
9209
+ },
9210
+
9211
+ /* Erase the input field and hide the date picker. */
9212
+ _clearDate: function(id) {
9213
+ var target = $(id);
9214
+ var inst = this._getInst(target[0]);
9215
+ this._selectDate(target, '');
9216
+ },
9217
+
9218
+ /* Update the input field with the selected date. */
9219
+ _selectDate: function(id, dateStr) {
9220
+ var target = $(id);
9221
+ var inst = this._getInst(target[0]);
9222
+ dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
9223
+ if (inst.input)
9224
+ inst.input.val(dateStr);
9225
+ this._updateAlternate(inst);
9226
+ var onSelect = this._get(inst, 'onSelect');
9227
+ if (onSelect)
9228
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
9229
+ else if (inst.input)
9230
+ inst.input.trigger('change'); // fire the change event
9231
+ if (inst.inline)
9232
+ this._updateDatepicker(inst);
9233
+ else {
9234
+ this._hideDatepicker();
9235
+ this._lastInput = inst.input[0];
9236
+ if (typeof(inst.input[0]) != 'object')
9237
+ inst.input.focus(); // restore focus
9238
+ this._lastInput = null;
9239
+ }
9240
+ },
9241
+
9242
+ /* Update any alternate field to synchronise with the main field. */
9243
+ _updateAlternate: function(inst) {
9244
+ var altField = this._get(inst, 'altField');
9245
+ if (altField) { // update alternate field too
9246
+ var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
9247
+ var date = this._getDate(inst);
9248
+ var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
9249
+ $(altField).each(function() { $(this).val(dateStr); });
9250
+ }
9251
+ },
9252
+
9253
+ /* Set as beforeShowDay function to prevent selection of weekends.
9254
+ @param date Date - the date to customise
9255
+ @return [boolean, string] - is this date selectable?, what is its CSS class? */
9256
+ noWeekends: function(date) {
9257
+ var day = date.getDay();
9258
+ return [(day > 0 && day < 6), ''];
9259
+ },
9260
+
9261
+ /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
9262
+ @param date Date - the date to get the week for
9263
+ @return number - the number of the week within the year that contains this date */
9264
+ iso8601Week: function(date) {
9265
+ var checkDate = new Date(date.getTime());
9266
+ // Find Thursday of this week starting on Monday
9267
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
9268
+ var time = checkDate.getTime();
9269
+ checkDate.setMonth(0); // Compare with Jan 1
9270
+ checkDate.setDate(1);
9271
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
9272
+ },
9273
+
9274
+ /* Parse a string value into a date object.
9275
+ See formatDate below for the possible formats.
9276
+
9277
+ @param format string - the expected format of the date
9278
+ @param value string - the date in the above format
9279
+ @param settings Object - attributes include:
9280
+ shortYearCutoff number - the cutoff year for determining the century (optional)
9281
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
9282
+ dayNames string[7] - names of the days from Sunday (optional)
9283
+ monthNamesShort string[12] - abbreviated names of the months (optional)
9284
+ monthNames string[12] - names of the months (optional)
9285
+ @return Date - the extracted date value or null if value is blank */
9286
+ parseDate: function (format, value, settings) {
9287
+ if (format == null || value == null)
9288
+ throw 'Invalid arguments';
9289
+ value = (typeof value == 'object' ? value.toString() : value + '');
9290
+ if (value == '')
9291
+ return null;
9292
+ var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
9293
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
9294
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
9295
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
9296
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
9297
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
9298
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
9299
+ var year = -1;
9300
+ var month = -1;
9301
+ var day = -1;
9302
+ var doy = -1;
9303
+ var literal = false;
9304
+ // Check whether a format character is doubled
9305
+ var lookAhead = function(match) {
9306
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
9307
+ if (matches)
9308
+ iFormat++;
9309
+ return matches;
9310
+ };
9311
+ // Extract a number from the string value
9312
+ var getNumber = function(match) {
9313
+ var isDoubled = lookAhead(match);
9314
+ var size = (match == '@' ? 14 : (match == '!' ? 20 :
9315
+ (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
9316
+ var digits = new RegExp('^\\d{1,' + size + '}');
9317
+ var num = value.substring(iValue).match(digits);
9318
+ if (!num)
9319
+ throw 'Missing number at position ' + iValue;
9320
+ iValue += num[0].length;
9321
+ return parseInt(num[0], 10);
9322
+ };
9323
+ // Extract a name from the string value and convert to an index
9324
+ var getName = function(match, shortNames, longNames) {
9325
+ var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
9326
+ return [ [k, v] ];
9327
+ }).sort(function (a, b) {
9328
+ return -(a[1].length - b[1].length);
9329
+ });
9330
+ var index = -1;
9331
+ $.each(names, function (i, pair) {
9332
+ var name = pair[1];
9333
+ if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
9334
+ index = pair[0];
9335
+ iValue += name.length;
9336
+ return false;
9337
+ }
9338
+ });
9339
+ if (index != -1)
9340
+ return index + 1;
9341
+ else
9342
+ throw 'Unknown name at position ' + iValue;
9343
+ };
9344
+ // Confirm that a literal character matches the string value
9345
+ var checkLiteral = function() {
9346
+ if (value.charAt(iValue) != format.charAt(iFormat))
9347
+ throw 'Unexpected literal at position ' + iValue;
9348
+ iValue++;
9349
+ };
9350
+ var iValue = 0;
9351
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
9352
+ if (literal)
9353
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
9354
+ literal = false;
9355
+ else
9356
+ checkLiteral();
9357
+ else
9358
+ switch (format.charAt(iFormat)) {
9359
+ case 'd':
9360
+ day = getNumber('d');
9361
+ break;
9362
+ case 'D':
9363
+ getName('D', dayNamesShort, dayNames);
9364
+ break;
9365
+ case 'o':
9366
+ doy = getNumber('o');
9367
+ break;
9368
+ case 'm':
9369
+ month = getNumber('m');
9370
+ break;
9371
+ case 'M':
9372
+ month = getName('M', monthNamesShort, monthNames);
9373
+ break;
9374
+ case 'y':
9375
+ year = getNumber('y');
9376
+ break;
9377
+ case '@':
9378
+ var date = new Date(getNumber('@'));
9379
+ year = date.getFullYear();
9380
+ month = date.getMonth() + 1;
9381
+ day = date.getDate();
9382
+ break;
9383
+ case '!':
9384
+ var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
9385
+ year = date.getFullYear();
9386
+ month = date.getMonth() + 1;
9387
+ day = date.getDate();
9388
+ break;
9389
+ case "'":
9390
+ if (lookAhead("'"))
9391
+ checkLiteral();
9392
+ else
9393
+ literal = true;
9394
+ break;
9395
+ default:
9396
+ checkLiteral();
9397
+ }
9398
+ }
9399
+ if (iValue < value.length){
9400
+ throw "Extra/unparsed characters found in date: " + value.substring(iValue);
9401
+ }
9402
+ if (year == -1)
9403
+ year = new Date().getFullYear();
9404
+ else if (year < 100)
9405
+ year += new Date().getFullYear() - new Date().getFullYear() % 100 +
9406
+ (year <= shortYearCutoff ? 0 : -100);
9407
+ if (doy > -1) {
9408
+ month = 1;
9409
+ day = doy;
9410
+ do {
9411
+ var dim = this._getDaysInMonth(year, month - 1);
9412
+ if (day <= dim)
9413
+ break;
9414
+ month++;
9415
+ day -= dim;
9416
+ } while (true);
9417
+ }
9418
+ var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
9419
+ if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
9420
+ throw 'Invalid date'; // E.g. 31/02/00
9421
+ return date;
9422
+ },
9423
+
9424
+ /* Standard date formats. */
9425
+ ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
9426
+ COOKIE: 'D, dd M yy',
9427
+ ISO_8601: 'yy-mm-dd',
9428
+ RFC_822: 'D, d M y',
9429
+ RFC_850: 'DD, dd-M-y',
9430
+ RFC_1036: 'D, d M y',
9431
+ RFC_1123: 'D, d M yy',
9432
+ RFC_2822: 'D, d M yy',
9433
+ RSS: 'D, d M y', // RFC 822
9434
+ TICKS: '!',
9435
+ TIMESTAMP: '@',
9436
+ W3C: 'yy-mm-dd', // ISO 8601
9437
+
9438
+ _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
9439
+ Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
9440
+
9441
+ /* Format a date object into a string value.
9442
+ The format can be combinations of the following:
9443
+ d - day of month (no leading zero)
9444
+ dd - day of month (two digit)
9445
+ o - day of year (no leading zeros)
9446
+ oo - day of year (three digit)
9447
+ D - day name short
9448
+ DD - day name long
9449
+ m - month of year (no leading zero)
9450
+ mm - month of year (two digit)
9451
+ M - month name short
9452
+ MM - month name long
9453
+ y - year (two digit)
9454
+ yy - year (four digit)
9455
+ @ - Unix timestamp (ms since 01/01/1970)
9456
+ ! - Windows ticks (100ns since 01/01/0001)
9457
+ '...' - literal text
9458
+ '' - single quote
9459
+
9460
+ @param format string - the desired format of the date
9461
+ @param date Date - the date value to format
9462
+ @param settings Object - attributes include:
9463
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
9464
+ dayNames string[7] - names of the days from Sunday (optional)
9465
+ monthNamesShort string[12] - abbreviated names of the months (optional)
9466
+ monthNames string[12] - names of the months (optional)
9467
+ @return string - the date in the above format */
9468
+ formatDate: function (format, date, settings) {
9469
+ if (!date)
9470
+ return '';
9471
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
9472
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
9473
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
9474
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
9475
+ // Check whether a format character is doubled
9476
+ var lookAhead = function(match) {
9477
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
9478
+ if (matches)
9479
+ iFormat++;
9480
+ return matches;
9481
+ };
9482
+ // Format a number, with leading zero if necessary
9483
+ var formatNumber = function(match, value, len) {
9484
+ var num = '' + value;
9485
+ if (lookAhead(match))
9486
+ while (num.length < len)
9487
+ num = '0' + num;
9488
+ return num;
9489
+ };
9490
+ // Format a name, short or long as requested
9491
+ var formatName = function(match, value, shortNames, longNames) {
9492
+ return (lookAhead(match) ? longNames[value] : shortNames[value]);
9493
+ };
9494
+ var output = '';
9495
+ var literal = false;
9496
+ if (date)
9497
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
9498
+ if (literal)
9499
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
9500
+ literal = false;
9501
+ else
9502
+ output += format.charAt(iFormat);
9503
+ else
9504
+ switch (format.charAt(iFormat)) {
9505
+ case 'd':
9506
+ output += formatNumber('d', date.getDate(), 2);
9507
+ break;
9508
+ case 'D':
9509
+ output += formatName('D', date.getDay(), dayNamesShort, dayNames);
9510
+ break;
9511
+ case 'o':
9512
+ output += formatNumber('o',
9513
+ Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
9514
+ break;
9515
+ case 'm':
9516
+ output += formatNumber('m', date.getMonth() + 1, 2);
9517
+ break;
9518
+ case 'M':
9519
+ output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
9520
+ break;
9521
+ case 'y':
9522
+ output += (lookAhead('y') ? date.getFullYear() :
9523
+ (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
9524
+ break;
9525
+ case '@':
9526
+ output += date.getTime();
9527
+ break;
9528
+ case '!':
9529
+ output += date.getTime() * 10000 + this._ticksTo1970;
9530
+ break;
9531
+ case "'":
9532
+ if (lookAhead("'"))
9533
+ output += "'";
9534
+ else
9535
+ literal = true;
9536
+ break;
9537
+ default:
9538
+ output += format.charAt(iFormat);
9539
+ }
9540
+ }
9541
+ return output;
9542
+ },
9543
+
9544
+ /* Extract all possible characters from the date format. */
9545
+ _possibleChars: function (format) {
9546
+ var chars = '';
9547
+ var literal = false;
9548
+ // Check whether a format character is doubled
9549
+ var lookAhead = function(match) {
9550
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
9551
+ if (matches)
9552
+ iFormat++;
9553
+ return matches;
9554
+ };
9555
+ for (var iFormat = 0; iFormat < format.length; iFormat++)
9556
+ if (literal)
9557
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
9558
+ literal = false;
9559
+ else
9560
+ chars += format.charAt(iFormat);
9561
+ else
9562
+ switch (format.charAt(iFormat)) {
9563
+ case 'd': case 'm': case 'y': case '@':
9564
+ chars += '0123456789';
9565
+ break;
9566
+ case 'D': case 'M':
9567
+ return null; // Accept anything
9568
+ case "'":
9569
+ if (lookAhead("'"))
9570
+ chars += "'";
9571
+ else
9572
+ literal = true;
9573
+ break;
9574
+ default:
9575
+ chars += format.charAt(iFormat);
9576
+ }
9577
+ return chars;
9578
+ },
9579
+
9580
+ /* Get a setting value, defaulting if necessary. */
9581
+ _get: function(inst, name) {
9582
+ return inst.settings[name] !== undefined ?
9583
+ inst.settings[name] : this._defaults[name];
9584
+ },
9585
+
9586
+ /* Parse existing date and initialise date picker. */
9587
+ _setDateFromField: function(inst, noDefault) {
9588
+ if (inst.input.val() == inst.lastVal) {
9589
+ return;
9590
+ }
9591
+ var dateFormat = this._get(inst, 'dateFormat');
9592
+ var dates = inst.lastVal = inst.input ? inst.input.val() : null;
9593
+ var date, defaultDate;
9594
+ date = defaultDate = this._getDefaultDate(inst);
9595
+ var settings = this._getFormatConfig(inst);
9596
+ try {
9597
+ date = this.parseDate(dateFormat, dates, settings) || defaultDate;
9598
+ } catch (event) {
9599
+ this.log(event);
9600
+ dates = (noDefault ? '' : dates);
9601
+ }
9602
+ inst.selectedDay = date.getDate();
9603
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
9604
+ inst.drawYear = inst.selectedYear = date.getFullYear();
9605
+ inst.currentDay = (dates ? date.getDate() : 0);
9606
+ inst.currentMonth = (dates ? date.getMonth() : 0);
9607
+ inst.currentYear = (dates ? date.getFullYear() : 0);
9608
+ this._adjustInstDate(inst);
9609
+ },
9610
+
9611
+ /* Retrieve the default date shown on opening. */
9612
+ _getDefaultDate: function(inst) {
9613
+ return this._restrictMinMax(inst,
9614
+ this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
9615
+ },
9616
+
9617
+ /* A date may be specified as an exact value or a relative one. */
9618
+ _determineDate: function(inst, date, defaultDate) {
9619
+ var offsetNumeric = function(offset) {
9620
+ var date = new Date();
9621
+ date.setDate(date.getDate() + offset);
9622
+ return date;
9623
+ };
9624
+ var offsetString = function(offset) {
9625
+ try {
9626
+ return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
9627
+ offset, $.datepicker._getFormatConfig(inst));
9628
+ }
9629
+ catch (e) {
9630
+ // Ignore
9631
+ }
9632
+ var date = (offset.toLowerCase().match(/^c/) ?
9633
+ $.datepicker._getDate(inst) : null) || new Date();
9634
+ var year = date.getFullYear();
9635
+ var month = date.getMonth();
9636
+ var day = date.getDate();
9637
+ var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
9638
+ var matches = pattern.exec(offset);
9639
+ while (matches) {
9640
+ switch (matches[2] || 'd') {
9641
+ case 'd' : case 'D' :
9642
+ day += parseInt(matches[1],10); break;
9643
+ case 'w' : case 'W' :
9644
+ day += parseInt(matches[1],10) * 7; break;
9645
+ case 'm' : case 'M' :
9646
+ month += parseInt(matches[1],10);
9647
+ day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
9648
+ break;
9649
+ case 'y': case 'Y' :
9650
+ year += parseInt(matches[1],10);
9651
+ day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
9652
+ break;
9653
+ }
9654
+ matches = pattern.exec(offset);
9655
+ }
9656
+ return new Date(year, month, day);
9657
+ };
9658
+ var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
9659
+ (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
9660
+ newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
9661
+ if (newDate) {
9662
+ newDate.setHours(0);
9663
+ newDate.setMinutes(0);
9664
+ newDate.setSeconds(0);
9665
+ newDate.setMilliseconds(0);
9666
+ }
9667
+ return this._daylightSavingAdjust(newDate);
9668
+ },
9669
+
9670
+ /* Handle switch to/from daylight saving.
9671
+ Hours may be non-zero on daylight saving cut-over:
9672
+ > 12 when midnight changeover, but then cannot generate
9673
+ midnight datetime, so jump to 1AM, otherwise reset.
9674
+ @param date (Date) the date to check
9675
+ @return (Date) the corrected date */
9676
+ _daylightSavingAdjust: function(date) {
9677
+ if (!date) return null;
9678
+ date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
9679
+ return date;
9680
+ },
9681
+
9682
+ /* Set the date(s) directly. */
9683
+ _setDate: function(inst, date, noChange) {
9684
+ var clear = !date;
9685
+ var origMonth = inst.selectedMonth;
9686
+ var origYear = inst.selectedYear;
9687
+ var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
9688
+ inst.selectedDay = inst.currentDay = newDate.getDate();
9689
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
9690
+ inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
9691
+ if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
9692
+ this._notifyChange(inst);
9693
+ this._adjustInstDate(inst);
9694
+ if (inst.input) {
9695
+ inst.input.val(clear ? '' : this._formatDate(inst));
9696
+ }
9697
+ },
9698
+
9699
+ /* Retrieve the date(s) directly. */
9700
+ _getDate: function(inst) {
9701
+ var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
9702
+ this._daylightSavingAdjust(new Date(
9703
+ inst.currentYear, inst.currentMonth, inst.currentDay)));
9704
+ return startDate;
9705
+ },
9706
+
9707
+ /* Generate the HTML for the current state of the date picker. */
9708
+ _generateHTML: function(inst) {
9709
+ var today = new Date();
9710
+ today = this._daylightSavingAdjust(
9711
+ new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
9712
+ var isRTL = this._get(inst, 'isRTL');
9713
+ var showButtonPanel = this._get(inst, 'showButtonPanel');
9714
+ var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
9715
+ var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
9716
+ var numMonths = this._getNumberOfMonths(inst);
9717
+ var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
9718
+ var stepMonths = this._get(inst, 'stepMonths');
9719
+ var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
9720
+ var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
9721
+ new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
9722
+ var minDate = this._getMinMaxDate(inst, 'min');
9723
+ var maxDate = this._getMinMaxDate(inst, 'max');
9724
+ var drawMonth = inst.drawMonth - showCurrentAtPos;
9725
+ var drawYear = inst.drawYear;
9726
+ if (drawMonth < 0) {
9727
+ drawMonth += 12;
9728
+ drawYear--;
9729
+ }
9730
+ if (maxDate) {
9731
+ var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
9732
+ maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
9733
+ maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
9734
+ while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
9735
+ drawMonth--;
9736
+ if (drawMonth < 0) {
9737
+ drawMonth = 11;
9738
+ drawYear--;
9739
+ }
9740
+ }
9741
+ }
9742
+ inst.drawMonth = drawMonth;
9743
+ inst.drawYear = drawYear;
9744
+ var prevText = this._get(inst, 'prevText');
9745
+ prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
9746
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
9747
+ this._getFormatConfig(inst)));
9748
+ var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
9749
+ '<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_' + dpuuid +
9750
+ '.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
9751
+ ' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
9752
+ (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
9753
+ var nextText = this._get(inst, 'nextText');
9754
+ nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
9755
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
9756
+ this._getFormatConfig(inst)));
9757
+ var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
9758
+ '<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_' + dpuuid +
9759
+ '.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
9760
+ ' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
9761
+ (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
9762
+ var currentText = this._get(inst, 'currentText');
9763
+ var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
9764
+ currentText = (!navigationAsDateFormat ? currentText :
9765
+ this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
9766
+ var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
9767
+ '.datepicker._hideDatepicker();">' + this._get(inst, 'closeText') + '</button>' : '');
9768
+ var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
9769
+ (this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
9770
+ '.datepicker._gotoToday(\'#' + inst.id + '\');"' +
9771
+ '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
9772
+ var firstDay = parseInt(this._get(inst, 'firstDay'),10);
9773
+ firstDay = (isNaN(firstDay) ? 0 : firstDay);
9774
+ var showWeek = this._get(inst, 'showWeek');
9775
+ var dayNames = this._get(inst, 'dayNames');
9776
+ var dayNamesShort = this._get(inst, 'dayNamesShort');
9777
+ var dayNamesMin = this._get(inst, 'dayNamesMin');
9778
+ var monthNames = this._get(inst, 'monthNames');
9779
+ var monthNamesShort = this._get(inst, 'monthNamesShort');
9780
+ var beforeShowDay = this._get(inst, 'beforeShowDay');
9781
+ var showOtherMonths = this._get(inst, 'showOtherMonths');
9782
+ var selectOtherMonths = this._get(inst, 'selectOtherMonths');
9783
+ var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
9784
+ var defaultDate = this._getDefaultDate(inst);
9785
+ var html = '';
9786
+ for (var row = 0; row < numMonths[0]; row++) {
9787
+ var group = '';
9788
+ this.maxRows = 4;
9789
+ for (var col = 0; col < numMonths[1]; col++) {
9790
+ var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
9791
+ var cornerClass = ' ui-corner-all';
9792
+ var calender = '';
9793
+ if (isMultiMonth) {
9794
+ calender += '<div class="ui-datepicker-group';
9795
+ if (numMonths[1] > 1)
9796
+ switch (col) {
9797
+ case 0: calender += ' ui-datepicker-group-first';
9798
+ cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
9799
+ case numMonths[1]-1: calender += ' ui-datepicker-group-last';
9800
+ cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
9801
+ default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
9802
+ }
9803
+ calender += '">';
9804
+ }
9805
+ calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
9806
+ (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
9807
+ (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
9808
+ this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
9809
+ row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
9810
+ '</div><table class="ui-datepicker-calendar"><thead>' +
9811
+ '<tr>';
9812
+ var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
9813
+ for (var dow = 0; dow < 7; dow++) { // days of the week
9814
+ var day = (dow + firstDay) % 7;
9815
+ thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
9816
+ '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
9817
+ }
9818
+ calender += thead + '</tr></thead><tbody>';
9819
+ var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
9820
+ if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
9821
+ inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
9822
+ var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
9823
+ var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
9824
+ var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
9825
+ this.maxRows = numRows;
9826
+ var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
9827
+ for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
9828
+ calender += '<tr>';
9829
+ var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
9830
+ this._get(inst, 'calculateWeek')(printDate) + '</td>');
9831
+ for (var dow = 0; dow < 7; dow++) { // create date picker days
9832
+ var daySettings = (beforeShowDay ?
9833
+ beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
9834
+ var otherMonth = (printDate.getMonth() != drawMonth);
9835
+ var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
9836
+ (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
9837
+ tbody += '<td class="' +
9838
+ ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
9839
+ (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
9840
+ ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
9841
+ (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
9842
+ // or defaultDate is current printedDate and defaultDate is selectedDate
9843
+ ' ' + this._dayOverClass : '') + // highlight selected day
9844
+ (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable days
9845
+ (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
9846
+ (printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
9847
+ (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
9848
+ ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
9849
+ (unselectable ? '' : ' onclick="DP_jQuery_' + dpuuid + '.datepicker._selectDay(\'#' +
9850
+ inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ', this);return false;"') + '>' + // actions
9851
+ (otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months
9852
+ (unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
9853
+ (printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
9854
+ (printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
9855
+ (otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
9856
+ '" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
9857
+ printDate.setDate(printDate.getDate() + 1);
9858
+ printDate = this._daylightSavingAdjust(printDate);
9859
+ }
9860
+ calender += tbody + '</tr>';
9861
+ }
9862
+ drawMonth++;
9863
+ if (drawMonth > 11) {
9864
+ drawMonth = 0;
9865
+ drawYear++;
9866
+ }
9867
+ calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
9868
+ ((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
9869
+ group += calender;
9870
+ }
9871
+ html += group;
9872
+ }
9873
+ html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
9874
+ '<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
9875
+ inst._keyEvent = false;
9876
+ return html;
9877
+ },
9878
+
9879
+ /* Generate the month and year header. */
9880
+ _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
9881
+ secondary, monthNames, monthNamesShort) {
9882
+ var changeMonth = this._get(inst, 'changeMonth');
9883
+ var changeYear = this._get(inst, 'changeYear');
9884
+ var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
9885
+ var html = '<div class="ui-datepicker-title">';
9886
+ var monthHtml = '';
9887
+ // month selection
9888
+ if (secondary || !changeMonth)
9889
+ monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
9890
+ else {
9891
+ var inMinYear = (minDate && minDate.getFullYear() == drawYear);
9892
+ var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
9893
+ monthHtml += '<select class="ui-datepicker-month" ' +
9894
+ 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
9895
+ '>';
9896
+ for (var month = 0; month < 12; month++) {
9897
+ if ((!inMinYear || month >= minDate.getMonth()) &&
9898
+ (!inMaxYear || month <= maxDate.getMonth()))
9899
+ monthHtml += '<option value="' + month + '"' +
9900
+ (month == drawMonth ? ' selected="selected"' : '') +
9901
+ '>' + monthNamesShort[month] + '</option>';
9902
+ }
9903
+ monthHtml += '</select>';
9904
+ }
9905
+ if (!showMonthAfterYear)
9906
+ html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
9907
+ // year selection
9908
+ if ( !inst.yearshtml ) {
9909
+ inst.yearshtml = '';
9910
+ if (secondary || !changeYear)
9911
+ html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
9912
+ else {
9913
+ // determine range of years to display
9914
+ var years = this._get(inst, 'yearRange').split(':');
9915
+ var thisYear = new Date().getFullYear();
9916
+ var determineYear = function(value) {
9917
+ var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
9918
+ (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
9919
+ parseInt(value, 10)));
9920
+ return (isNaN(year) ? thisYear : year);
9921
+ };
9922
+ var year = determineYear(years[0]);
9923
+ var endYear = Math.max(year, determineYear(years[1] || ''));
9924
+ year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
9925
+ endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
9926
+ inst.yearshtml += '<select class="ui-datepicker-year" ' +
9927
+ 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
9928
+ '>';
9929
+ for (; year <= endYear; year++) {
9930
+ inst.yearshtml += '<option value="' + year + '"' +
9931
+ (year == drawYear ? ' selected="selected"' : '') +
9932
+ '>' + year + '</option>';
9933
+ }
9934
+ inst.yearshtml += '</select>';
9935
+
9936
+ html += inst.yearshtml;
9937
+ inst.yearshtml = null;
9938
+ }
9939
+ }
9940
+ html += this._get(inst, 'yearSuffix');
9941
+ if (showMonthAfterYear)
9942
+ html += (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '') + monthHtml;
9943
+ html += '</div>'; // Close datepicker_header
9944
+ return html;
9945
+ },
9946
+
9947
+ /* Adjust one of the date sub-fields. */
9948
+ _adjustInstDate: function(inst, offset, period) {
9949
+ var year = inst.drawYear + (period == 'Y' ? offset : 0);
9950
+ var month = inst.drawMonth + (period == 'M' ? offset : 0);
9951
+ var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
9952
+ (period == 'D' ? offset : 0);
9953
+ var date = this._restrictMinMax(inst,
9954
+ this._daylightSavingAdjust(new Date(year, month, day)));
9955
+ inst.selectedDay = date.getDate();
9956
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
9957
+ inst.drawYear = inst.selectedYear = date.getFullYear();
9958
+ if (period == 'M' || period == 'Y')
9959
+ this._notifyChange(inst);
9960
+ },
9961
+
9962
+ /* Ensure a date is within any min/max bounds. */
9963
+ _restrictMinMax: function(inst, date) {
9964
+ var minDate = this._getMinMaxDate(inst, 'min');
9965
+ var maxDate = this._getMinMaxDate(inst, 'max');
9966
+ var newDate = (minDate && date < minDate ? minDate : date);
9967
+ newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
9968
+ return newDate;
9969
+ },
9970
+
9971
+ /* Notify change of month/year. */
9972
+ _notifyChange: function(inst) {
9973
+ var onChange = this._get(inst, 'onChangeMonthYear');
9974
+ if (onChange)
9975
+ onChange.apply((inst.input ? inst.input[0] : null),
9976
+ [inst.selectedYear, inst.selectedMonth + 1, inst]);
9977
+ },
9978
+
9979
+ /* Determine the number of months to show. */
9980
+ _getNumberOfMonths: function(inst) {
9981
+ var numMonths = this._get(inst, 'numberOfMonths');
9982
+ return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
9983
+ },
9984
+
9985
+ /* Determine the current maximum date - ensure no time components are set. */
9986
+ _getMinMaxDate: function(inst, minMax) {
9987
+ return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
9988
+ },
9989
+
9990
+ /* Find the number of days in a given month. */
9991
+ _getDaysInMonth: function(year, month) {
9992
+ return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
9993
+ },
9994
+
9995
+ /* Find the day of the week of the first of a month. */
9996
+ _getFirstDayOfMonth: function(year, month) {
9997
+ return new Date(year, month, 1).getDay();
9998
+ },
9999
+
10000
+ /* Determines if we should allow a "next/prev" month display change. */
10001
+ _canAdjustMonth: function(inst, offset, curYear, curMonth) {
10002
+ var numMonths = this._getNumberOfMonths(inst);
10003
+ var date = this._daylightSavingAdjust(new Date(curYear,
10004
+ curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
10005
+ if (offset < 0)
10006
+ date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
10007
+ return this._isInRange(inst, date);
10008
+ },
10009
+
10010
+ /* Is the given date in the accepted range? */
10011
+ _isInRange: function(inst, date) {
10012
+ var minDate = this._getMinMaxDate(inst, 'min');
10013
+ var maxDate = this._getMinMaxDate(inst, 'max');
10014
+ return ((!minDate || date.getTime() >= minDate.getTime()) &&
10015
+ (!maxDate || date.getTime() <= maxDate.getTime()));
10016
+ },
10017
+
10018
+ /* Provide the configuration settings for formatting/parsing. */
10019
+ _getFormatConfig: function(inst) {
10020
+ var shortYearCutoff = this._get(inst, 'shortYearCutoff');
10021
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
10022
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
10023
+ return {shortYearCutoff: shortYearCutoff,
10024
+ dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
10025
+ monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
10026
+ },
10027
+
10028
+ /* Format the given date for display. */
10029
+ _formatDate: function(inst, day, month, year) {
10030
+ if (!day) {
10031
+ inst.currentDay = inst.selectedDay;
10032
+ inst.currentMonth = inst.selectedMonth;
10033
+ inst.currentYear = inst.selectedYear;
10034
+ }
10035
+ var date = (day ? (typeof day == 'object' ? day :
10036
+ this._daylightSavingAdjust(new Date(year, month, day))) :
10037
+ this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
10038
+ return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
10039
+ }
10040
+ });
10041
+
10042
+ /*
10043
+ * Bind hover events for datepicker elements.
10044
+ * Done via delegate so the binding only occurs once in the lifetime of the parent div.
10045
+ * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
10046
+ */
10047
+ function bindHover(dpDiv) {
10048
+ var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
10049
+ return dpDiv.bind('mouseout', function(event) {
10050
+ var elem = $( event.target ).closest( selector );
10051
+ if ( !elem.length ) {
10052
+ return;
10053
+ }
10054
+ elem.removeClass( "ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover" );
10055
+ })
10056
+ .bind('mouseover', function(event) {
10057
+ var elem = $( event.target ).closest( selector );
10058
+ if ($.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0]) ||
10059
+ !elem.length ) {
10060
+ return;
10061
+ }
10062
+ elem.parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
10063
+ elem.addClass('ui-state-hover');
10064
+ if (elem.hasClass('ui-datepicker-prev')) elem.addClass('ui-datepicker-prev-hover');
10065
+ if (elem.hasClass('ui-datepicker-next')) elem.addClass('ui-datepicker-next-hover');
10066
+ });
10067
+ }
10068
+
10069
+ /* jQuery extend now ignores nulls! */
10070
+ function extendRemove(target, props) {
10071
+ $.extend(target, props);
10072
+ for (var name in props)
10073
+ if (props[name] == null || props[name] == undefined)
10074
+ target[name] = props[name];
10075
+ return target;
10076
+ };
10077
+
10078
+ /* Determine whether an object is an array. */
10079
+ function isArray(a) {
10080
+ return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
10081
+ (a.constructor && a.constructor.toString().match(/\Array\(\)/))));
10082
+ };
10083
+
10084
+ /* Invoke the datepicker functionality.
10085
+ @param options string - a command, optionally followed by additional parameters or
10086
+ Object - settings for attaching new datepicker functionality
10087
+ @return jQuery object */
10088
+ $.fn.datepicker = function(options){
10089
+
10090
+ /* Verify an empty collection wasn't passed - Fixes #6976 */
10091
+ if ( !this.length ) {
10092
+ return this;
10093
+ }
10094
+
10095
+ /* Initialise the date picker. */
10096
+ if (!$.datepicker.initialized) {
10097
+ $(document).mousedown($.datepicker._checkExternalClick).
10098
+ find('body').append($.datepicker.dpDiv);
10099
+ $.datepicker.initialized = true;
10100
+ }
10101
+
10102
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
10103
+ if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
10104
+ return $.datepicker['_' + options + 'Datepicker'].
10105
+ apply($.datepicker, [this[0]].concat(otherArgs));
10106
+ if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
10107
+ return $.datepicker['_' + options + 'Datepicker'].
10108
+ apply($.datepicker, [this[0]].concat(otherArgs));
10109
+ return this.each(function() {
10110
+ typeof options == 'string' ?
10111
+ $.datepicker['_' + options + 'Datepicker'].
10112
+ apply($.datepicker, [this].concat(otherArgs)) :
10113
+ $.datepicker._attachDatepicker(this, options);
10114
+ });
10115
+ };
10116
+
10117
+ $.datepicker = new Datepicker(); // singleton instance
10118
+ $.datepicker.initialized = false;
10119
+ $.datepicker.uuid = new Date().getTime();
10120
+ $.datepicker.version = "1.8.17";
10121
+
10122
+ // Workaround for #4055
10123
+ // Add another global to avoid noConflict issues with inline event handlers
10124
+ window['DP_jQuery_' + dpuuid] = $;
10125
+
10126
+ })(jQuery);
10127
+ /*
10128
+ * jQuery UI Progressbar 1.8.17
10129
+ *
10130
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
10131
+ * Dual licensed under the MIT or GPL Version 2 licenses.
10132
+ * http://jquery.org/license
10133
+ *
10134
+ * http://docs.jquery.com/UI/Progressbar
10135
+ *
10136
+ * Depends:
10137
+ * jquery.ui.core.js
10138
+ * jquery.ui.widget.js
10139
+ */
10140
+ (function( $, undefined ) {
10141
+
10142
+ $.widget( "ui.progressbar", {
10143
+ options: {
10144
+ value: 0,
10145
+ max: 100
10146
+ },
10147
+
10148
+ min: 0,
10149
+
10150
+ _create: function() {
10151
+ this.element
10152
+ .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
10153
+ .attr({
10154
+ role: "progressbar",
10155
+ "aria-valuemin": this.min,
10156
+ "aria-valuemax": this.options.max,
10157
+ "aria-valuenow": this._value()
10158
+ });
10159
+
10160
+ this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
10161
+ .appendTo( this.element );
10162
+
10163
+ this.oldValue = this._value();
10164
+ this._refreshValue();
10165
+ },
10166
+
10167
+ destroy: function() {
10168
+ this.element
10169
+ .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
10170
+ .removeAttr( "role" )
10171
+ .removeAttr( "aria-valuemin" )
10172
+ .removeAttr( "aria-valuemax" )
10173
+ .removeAttr( "aria-valuenow" );
10174
+
10175
+ this.valueDiv.remove();
10176
+
10177
+ $.Widget.prototype.destroy.apply( this, arguments );
10178
+ },
10179
+
10180
+ value: function( newValue ) {
10181
+ if ( newValue === undefined ) {
10182
+ return this._value();
10183
+ }
10184
+
10185
+ this._setOption( "value", newValue );
10186
+ return this;
10187
+ },
10188
+
10189
+ _setOption: function( key, value ) {
10190
+ if ( key === "value" ) {
10191
+ this.options.value = value;
10192
+ this._refreshValue();
10193
+ if ( this._value() === this.options.max ) {
10194
+ this._trigger( "complete" );
10195
+ }
10196
+ }
10197
+
10198
+ $.Widget.prototype._setOption.apply( this, arguments );
10199
+ },
10200
+
10201
+ _value: function() {
10202
+ var val = this.options.value;
10203
+ // normalize invalid value
10204
+ if ( typeof val !== "number" ) {
10205
+ val = 0;
10206
+ }
10207
+ return Math.min( this.options.max, Math.max( this.min, val ) );
10208
+ },
10209
+
10210
+ _percentage: function() {
10211
+ return 100 * this._value() / this.options.max;
10212
+ },
10213
+
10214
+ _refreshValue: function() {
10215
+ var value = this.value();
10216
+ var percentage = this._percentage();
10217
+
10218
+ if ( this.oldValue !== value ) {
10219
+ this.oldValue = value;
10220
+ this._trigger( "change" );
10221
+ }
10222
+
10223
+ this.valueDiv
10224
+ .toggle( value > this.min )
10225
+ .toggleClass( "ui-corner-right", value === this.options.max )
10226
+ .width( percentage.toFixed(0) + "%" );
10227
+ this.element.attr( "aria-valuenow", value );
10228
+ }
10229
+ });
10230
+
10231
+ $.extend( $.ui.progressbar, {
10232
+ version: "1.8.17"
10233
+ });
10234
+
10235
+ })( jQuery );
10236
+ /*
10237
+ * jQuery UI Effects 1.8.17
10238
+ *
10239
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
10240
+ * Dual licensed under the MIT or GPL Version 2 licenses.
10241
+ * http://jquery.org/license
10242
+ *
10243
+ * http://docs.jquery.com/UI/Effects/
10244
+ */
10245
+ ;jQuery.effects || (function($, undefined) {
10246
+
10247
+ $.effects = {};
10248
+
10249
+
10250
+
10251
+ /******************************************************************************/
10252
+ /****************************** COLOR ANIMATIONS ******************************/
10253
+ /******************************************************************************/
10254
+
10255
+ // override the animation for color styles
10256
+ $.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor',
10257
+ 'borderRightColor', 'borderTopColor', 'borderColor', 'color', 'outlineColor'],
10258
+ function(i, attr) {
10259
+ $.fx.step[attr] = function(fx) {
10260
+ if (!fx.colorInit) {
10261
+ fx.start = getColor(fx.elem, attr);
10262
+ fx.end = getRGB(fx.end);
10263
+ fx.colorInit = true;
10264
+ }
10265
+
10266
+ fx.elem.style[attr] = 'rgb(' +
10267
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + ',' +
10268
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + ',' +
10269
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ')';
10270
+ };
10271
+ });
10272
+
10273
+ // Color Conversion functions from highlightFade
10274
+ // By Blair Mitchelmore
10275
+ // http://jquery.offput.ca/highlightFade/
10276
+
10277
+ // Parse strings looking for color tuples [255,255,255]
10278
+ function getRGB(color) {
10279
+ var result;
10280
+
10281
+ // Check if we're already dealing with an array of colors
10282
+ if ( color && color.constructor == Array && color.length == 3 )
10283
+ return color;
10284
+
10285
+ // Look for rgb(num,num,num)
10286
+ if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
10287
+ return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
10288
+
10289
+ // Look for rgb(num%,num%,num%)
10290
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
10291
+ return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
10292
+
10293
+ // Look for #a0b1c2
10294
+ if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
10295
+ return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
10296
+
10297
+ // Look for #fff
10298
+ if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
10299
+ return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
10300
+
10301
+ // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
10302
+ if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
10303
+ return colors['transparent'];
10304
+
10305
+ // Otherwise, we're most likely dealing with a named color
10306
+ return colors[$.trim(color).toLowerCase()];
10307
+ }
10308
+
10309
+ function getColor(elem, attr) {
10310
+ var color;
10311
+
10312
+ do {
10313
+ color = $.curCSS(elem, attr);
10314
+
10315
+ // Keep going until we find an element that has color, or we hit the body
10316
+ if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") )
10317
+ break;
10318
+
10319
+ attr = "backgroundColor";
10320
+ } while ( elem = elem.parentNode );
10321
+
10322
+ return getRGB(color);
10323
+ };
10324
+
10325
+ // Some named colors to work with
10326
+ // From Interface by Stefan Petre
10327
+ // http://interface.eyecon.ro/
10328
+
10329
+ var colors = {
10330
+ aqua:[0,255,255],
10331
+ azure:[240,255,255],
10332
+ beige:[245,245,220],
10333
+ black:[0,0,0],
10334
+ blue:[0,0,255],
10335
+ brown:[165,42,42],
10336
+ cyan:[0,255,255],
10337
+ darkblue:[0,0,139],
10338
+ darkcyan:[0,139,139],
10339
+ darkgrey:[169,169,169],
10340
+ darkgreen:[0,100,0],
10341
+ darkkhaki:[189,183,107],
10342
+ darkmagenta:[139,0,139],
10343
+ darkolivegreen:[85,107,47],
10344
+ darkorange:[255,140,0],
10345
+ darkorchid:[153,50,204],
10346
+ darkred:[139,0,0],
10347
+ darksalmon:[233,150,122],
10348
+ darkviolet:[148,0,211],
10349
+ fuchsia:[255,0,255],
10350
+ gold:[255,215,0],
10351
+ green:[0,128,0],
10352
+ indigo:[75,0,130],
10353
+ khaki:[240,230,140],
10354
+ lightblue:[173,216,230],
10355
+ lightcyan:[224,255,255],
10356
+ lightgreen:[144,238,144],
10357
+ lightgrey:[211,211,211],
10358
+ lightpink:[255,182,193],
10359
+ lightyellow:[255,255,224],
10360
+ lime:[0,255,0],
10361
+ magenta:[255,0,255],
10362
+ maroon:[128,0,0],
10363
+ navy:[0,0,128],
10364
+ olive:[128,128,0],
10365
+ orange:[255,165,0],
10366
+ pink:[255,192,203],
10367
+ purple:[128,0,128],
10368
+ violet:[128,0,128],
10369
+ red:[255,0,0],
10370
+ silver:[192,192,192],
10371
+ white:[255,255,255],
10372
+ yellow:[255,255,0],
10373
+ transparent: [255,255,255]
10374
+ };
10375
+
10376
+
10377
+
10378
+ /******************************************************************************/
10379
+ /****************************** CLASS ANIMATIONS ******************************/
10380
+ /******************************************************************************/
10381
+
10382
+ var classAnimationActions = ['add', 'remove', 'toggle'],
10383
+ shorthandStyles = {
10384
+ border: 1,
10385
+ borderBottom: 1,
10386
+ borderColor: 1,
10387
+ borderLeft: 1,
10388
+ borderRight: 1,
10389
+ borderTop: 1,
10390
+ borderWidth: 1,
10391
+ margin: 1,
10392
+ padding: 1
10393
+ };
10394
+
10395
+ function getElementStyles() {
10396
+ var style = document.defaultView
10397
+ ? document.defaultView.getComputedStyle(this, null)
10398
+ : this.currentStyle,
10399
+ newStyle = {},
10400
+ key,
10401
+ camelCase;
10402
+
10403
+ // webkit enumerates style porperties
10404
+ if (style && style.length && style[0] && style[style[0]]) {
10405
+ var len = style.length;
10406
+ while (len--) {
10407
+ key = style[len];
10408
+ if (typeof style[key] == 'string') {
10409
+ camelCase = key.replace(/\-(\w)/g, function(all, letter){
10410
+ return letter.toUpperCase();
10411
+ });
10412
+ newStyle[camelCase] = style[key];
10413
+ }
10414
+ }
10415
+ } else {
10416
+ for (key in style) {
10417
+ if (typeof style[key] === 'string') {
10418
+ newStyle[key] = style[key];
10419
+ }
10420
+ }
10421
+ }
10422
+
10423
+ return newStyle;
10424
+ }
10425
+
10426
+ function filterStyles(styles) {
10427
+ var name, value;
10428
+ for (name in styles) {
10429
+ value = styles[name];
10430
+ if (
10431
+ // ignore null and undefined values
10432
+ value == null ||
10433
+ // ignore functions (when does this occur?)
10434
+ $.isFunction(value) ||
10435
+ // shorthand styles that need to be expanded
10436
+ name in shorthandStyles ||
10437
+ // ignore scrollbars (break in IE)
10438
+ (/scrollbar/).test(name) ||
10439
+
10440
+ // only colors or values that can be converted to numbers
10441
+ (!(/color/i).test(name) && isNaN(parseFloat(value)))
10442
+ ) {
10443
+ delete styles[name];
10444
+ }
10445
+ }
10446
+
10447
+ return styles;
10448
+ }
10449
+
10450
+ function styleDifference(oldStyle, newStyle) {
10451
+ var diff = { _: 0 }, // http://dev.jquery.com/ticket/5459
10452
+ name;
10453
+
10454
+ for (name in newStyle) {
10455
+ if (oldStyle[name] != newStyle[name]) {
10456
+ diff[name] = newStyle[name];
10457
+ }
10458
+ }
10459
+
10460
+ return diff;
10461
+ }
10462
+
10463
+ $.effects.animateClass = function(value, duration, easing, callback) {
10464
+ if ($.isFunction(easing)) {
10465
+ callback = easing;
10466
+ easing = null;
10467
+ }
10468
+
10469
+ return this.queue(function() {
10470
+ var that = $(this),
10471
+ originalStyleAttr = that.attr('style') || ' ',
10472
+ originalStyle = filterStyles(getElementStyles.call(this)),
10473
+ newStyle,
10474
+ className = that.attr('class');
10475
+
10476
+ $.each(classAnimationActions, function(i, action) {
10477
+ if (value[action]) {
10478
+ that[action + 'Class'](value[action]);
10479
+ }
10480
+ });
10481
+ newStyle = filterStyles(getElementStyles.call(this));
10482
+ that.attr('class', className);
10483
+
10484
+ that.animate(styleDifference(originalStyle, newStyle), {
10485
+ queue: false,
10486
+ duration: duration,
10487
+ easing: easing,
10488
+ complete: function() {
10489
+ $.each(classAnimationActions, function(i, action) {
10490
+ if (value[action]) { that[action + 'Class'](value[action]); }
10491
+ });
10492
+ // work around bug in IE by clearing the cssText before setting it
10493
+ if (typeof that.attr('style') == 'object') {
10494
+ that.attr('style').cssText = '';
10495
+ that.attr('style').cssText = originalStyleAttr;
10496
+ } else {
10497
+ that.attr('style', originalStyleAttr);
10498
+ }
10499
+ if (callback) { callback.apply(this, arguments); }
10500
+ $.dequeue( this );
10501
+ }
10502
+ });
10503
+ });
10504
+ };
10505
+
10506
+ $.fn.extend({
10507
+ _addClass: $.fn.addClass,
10508
+ addClass: function(classNames, speed, easing, callback) {
10509
+ return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames);
10510
+ },
10511
+
10512
+ _removeClass: $.fn.removeClass,
10513
+ removeClass: function(classNames,speed,easing,callback) {
10514
+ return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames);
10515
+ },
10516
+
10517
+ _toggleClass: $.fn.toggleClass,
10518
+ toggleClass: function(classNames, force, speed, easing, callback) {
10519
+ if ( typeof force == "boolean" || force === undefined ) {
10520
+ if ( !speed ) {
10521
+ // without speed parameter;
10522
+ return this._toggleClass(classNames, force);
10523
+ } else {
10524
+ return $.effects.animateClass.apply(this, [(force?{add:classNames}:{remove:classNames}),speed,easing,callback]);
10525
+ }
10526
+ } else {
10527
+ // without switch parameter;
10528
+ return $.effects.animateClass.apply(this, [{ toggle: classNames },force,speed,easing]);
10529
+ }
10530
+ },
10531
+
10532
+ switchClass: function(remove,add,speed,easing,callback) {
10533
+ return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]);
10534
+ }
10535
+ });
10536
+
10537
+
10538
+
10539
+ /******************************************************************************/
10540
+ /*********************************** EFFECTS **********************************/
10541
+ /******************************************************************************/
10542
+
10543
+ $.extend($.effects, {
10544
+ version: "1.8.17",
10545
+
10546
+ // Saves a set of properties in a data storage
10547
+ save: function(element, set) {
10548
+ for(var i=0; i < set.length; i++) {
10549
+ if(set[i] !== null) element.data("ec.storage."+set[i], element[0].style[set[i]]);
10550
+ }
10551
+ },
10552
+
10553
+ // Restores a set of previously saved properties from a data storage
10554
+ restore: function(element, set) {
10555
+ for(var i=0; i < set.length; i++) {
10556
+ if(set[i] !== null) element.css(set[i], element.data("ec.storage."+set[i]));
10557
+ }
10558
+ },
10559
+
10560
+ setMode: function(el, mode) {
10561
+ if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle
10562
+ return mode;
10563
+ },
10564
+
10565
+ getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value
10566
+ // this should be a little more flexible in the future to handle a string & hash
10567
+ var y, x;
10568
+ switch (origin[0]) {
10569
+ case 'top': y = 0; break;
10570
+ case 'middle': y = 0.5; break;
10571
+ case 'bottom': y = 1; break;
10572
+ default: y = origin[0] / original.height;
10573
+ };
10574
+ switch (origin[1]) {
10575
+ case 'left': x = 0; break;
10576
+ case 'center': x = 0.5; break;
10577
+ case 'right': x = 1; break;
10578
+ default: x = origin[1] / original.width;
10579
+ };
10580
+ return {x: x, y: y};
10581
+ },
10582
+
10583
+ // Wraps the element around a wrapper that copies position properties
10584
+ createWrapper: function(element) {
10585
+
10586
+ // if the element is already wrapped, return it
10587
+ if (element.parent().is('.ui-effects-wrapper')) {
10588
+ return element.parent();
10589
+ }
10590
+
10591
+ // wrap the element
10592
+ var props = {
10593
+ width: element.outerWidth(true),
10594
+ height: element.outerHeight(true),
10595
+ 'float': element.css('float')
10596
+ },
10597
+ wrapper = $('<div></div>')
10598
+ .addClass('ui-effects-wrapper')
10599
+ .css({
10600
+ fontSize: '100%',
10601
+ background: 'transparent',
10602
+ border: 'none',
10603
+ margin: 0,
10604
+ padding: 0
10605
+ }),
10606
+ active = document.activeElement;
10607
+
10608
+ element.wrap(wrapper);
10609
+
10610
+ // Fixes #7595 - Elements lose focus when wrapped.
10611
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
10612
+ $( active ).focus();
10613
+ }
10614
+
10615
+ wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element
10616
+
10617
+ // transfer positioning properties to the wrapper
10618
+ if (element.css('position') == 'static') {
10619
+ wrapper.css({ position: 'relative' });
10620
+ element.css({ position: 'relative' });
10621
+ } else {
10622
+ $.extend(props, {
10623
+ position: element.css('position'),
10624
+ zIndex: element.css('z-index')
10625
+ });
10626
+ $.each(['top', 'left', 'bottom', 'right'], function(i, pos) {
10627
+ props[pos] = element.css(pos);
10628
+ if (isNaN(parseInt(props[pos], 10))) {
10629
+ props[pos] = 'auto';
10630
+ }
10631
+ });
10632
+ element.css({position: 'relative', top: 0, left: 0, right: 'auto', bottom: 'auto' });
10633
+ }
10634
+
10635
+ return wrapper.css(props).show();
10636
+ },
10637
+
10638
+ removeWrapper: function(element) {
10639
+ var parent,
10640
+ active = document.activeElement;
10641
+
10642
+ if (element.parent().is('.ui-effects-wrapper')) {
10643
+ parent = element.parent().replaceWith(element);
10644
+ // Fixes #7595 - Elements lose focus when wrapped.
10645
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
10646
+ $( active ).focus();
10647
+ }
10648
+ return parent;
10649
+ }
10650
+
10651
+ return element;
10652
+ },
10653
+
10654
+ setTransition: function(element, list, factor, value) {
10655
+ value = value || {};
10656
+ $.each(list, function(i, x){
10657
+ unit = element.cssUnit(x);
10658
+ if (unit[0] > 0) value[x] = unit[0] * factor + unit[1];
10659
+ });
10660
+ return value;
10661
+ }
10662
+ });
10663
+
10664
+
10665
+ function _normalizeArguments(effect, options, speed, callback) {
10666
+ // shift params for method overloading
10667
+ if (typeof effect == 'object') {
10668
+ callback = options;
10669
+ speed = null;
10670
+ options = effect;
10671
+ effect = options.effect;
10672
+ }
10673
+ if ($.isFunction(options)) {
10674
+ callback = options;
10675
+ speed = null;
10676
+ options = {};
10677
+ }
10678
+ if (typeof options == 'number' || $.fx.speeds[options]) {
10679
+ callback = speed;
10680
+ speed = options;
10681
+ options = {};
10682
+ }
10683
+ if ($.isFunction(speed)) {
10684
+ callback = speed;
10685
+ speed = null;
10686
+ }
10687
+
10688
+ options = options || {};
10689
+
10690
+ speed = speed || options.duration;
10691
+ speed = $.fx.off ? 0 : typeof speed == 'number'
10692
+ ? speed : speed in $.fx.speeds ? $.fx.speeds[speed] : $.fx.speeds._default;
10693
+
10694
+ callback = callback || options.complete;
10695
+
10696
+ return [effect, options, speed, callback];
10697
+ }
10698
+
10699
+ function standardSpeed( speed ) {
10700
+ // valid standard speeds
10701
+ if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
10702
+ return true;
10703
+ }
10704
+
10705
+ // invalid strings - treat as "normal" speed
10706
+ if ( typeof speed === "string" && !$.effects[ speed ] ) {
10707
+ return true;
10708
+ }
10709
+
10710
+ return false;
10711
+ }
10712
+
10713
+ $.fn.extend({
10714
+ effect: function(effect, options, speed, callback) {
10715
+ var args = _normalizeArguments.apply(this, arguments),
10716
+ // TODO: make effects take actual parameters instead of a hash
10717
+ args2 = {
10718
+ options: args[1],
10719
+ duration: args[2],
10720
+ callback: args[3]
10721
+ },
10722
+ mode = args2.options.mode,
10723
+ effectMethod = $.effects[effect];
10724
+
10725
+ if ( $.fx.off || !effectMethod ) {
10726
+ // delegate to the original method (e.g., .show()) if possible
10727
+ if ( mode ) {
10728
+ return this[ mode ]( args2.duration, args2.callback );
10729
+ } else {
10730
+ return this.each(function() {
10731
+ if ( args2.callback ) {
10732
+ args2.callback.call( this );
10733
+ }
10734
+ });
10735
+ }
10736
+ }
10737
+
10738
+ return effectMethod.call(this, args2);
10739
+ },
10740
+
10741
+ _show: $.fn.show,
10742
+ show: function(speed) {
10743
+ if ( standardSpeed( speed ) ) {
10744
+ return this._show.apply(this, arguments);
10745
+ } else {
10746
+ var args = _normalizeArguments.apply(this, arguments);
10747
+ args[1].mode = 'show';
10748
+ return this.effect.apply(this, args);
10749
+ }
10750
+ },
10751
+
10752
+ _hide: $.fn.hide,
10753
+ hide: function(speed) {
10754
+ if ( standardSpeed( speed ) ) {
10755
+ return this._hide.apply(this, arguments);
10756
+ } else {
10757
+ var args = _normalizeArguments.apply(this, arguments);
10758
+ args[1].mode = 'hide';
10759
+ return this.effect.apply(this, args);
10760
+ }
10761
+ },
10762
+
10763
+ // jQuery core overloads toggle and creates _toggle
10764
+ __toggle: $.fn.toggle,
10765
+ toggle: function(speed) {
10766
+ if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
10767
+ return this.__toggle.apply(this, arguments);
10768
+ } else {
10769
+ var args = _normalizeArguments.apply(this, arguments);
10770
+ args[1].mode = 'toggle';
10771
+ return this.effect.apply(this, args);
10772
+ }
10773
+ },
10774
+
10775
+ // helper functions
10776
+ cssUnit: function(key) {
10777
+ var style = this.css(key), val = [];
10778
+ $.each( ['em','px','%','pt'], function(i, unit){
10779
+ if(style.indexOf(unit) > 0)
10780
+ val = [parseFloat(style), unit];
10781
+ });
10782
+ return val;
10783
+ }
10784
+ });
10785
+
10786
+
10787
+
10788
+ /******************************************************************************/
10789
+ /*********************************** EASING ***********************************/
10790
+ /******************************************************************************/
10791
+
10792
+ /*
10793
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
10794
+ *
10795
+ * Uses the built in easing capabilities added In jQuery 1.1
10796
+ * to offer multiple easing options
10797
+ *
10798
+ * TERMS OF USE - jQuery Easing
10799
+ *
10800
+ * Open source under the BSD License.
10801
+ *
10802
+ * Copyright 2008 George McGinley Smith
10803
+ * All rights reserved.
10804
+ *
10805
+ * Redistribution and use in source and binary forms, with or without modification,
10806
+ * are permitted provided that the following conditions are met:
10807
+ *
10808
+ * Redistributions of source code must retain the above copyright notice, this list of
10809
+ * conditions and the following disclaimer.
10810
+ * Redistributions in binary form must reproduce the above copyright notice, this list
10811
+ * of conditions and the following disclaimer in the documentation and/or other materials
10812
+ * provided with the distribution.
10813
+ *
10814
+ * Neither the name of the author nor the names of contributors may be used to endorse
10815
+ * or promote products derived from this software without specific prior written permission.
10816
+ *
10817
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
10818
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
10819
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
10820
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
10821
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
10822
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
10823
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
10824
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
10825
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
10826
+ *
10827
+ */
10828
+
10829
+ // t: current time, b: begInnIng value, c: change In value, d: duration
10830
+ $.easing.jswing = $.easing.swing;
10831
+
10832
+ $.extend($.easing,
10833
+ {
10834
+ def: 'easeOutQuad',
10835
+ swing: function (x, t, b, c, d) {
10836
+ //alert($.easing.default);
10837
+ return $.easing[$.easing.def](x, t, b, c, d);
10838
+ },
10839
+ easeInQuad: function (x, t, b, c, d) {
10840
+ return c*(t/=d)*t + b;
10841
+ },
10842
+ easeOutQuad: function (x, t, b, c, d) {
10843
+ return -c *(t/=d)*(t-2) + b;
10844
+ },
10845
+ easeInOutQuad: function (x, t, b, c, d) {
10846
+ if ((t/=d/2) < 1) return c/2*t*t + b;
10847
+ return -c/2 * ((--t)*(t-2) - 1) + b;
10848
+ },
10849
+ easeInCubic: function (x, t, b, c, d) {
10850
+ return c*(t/=d)*t*t + b;
10851
+ },
10852
+ easeOutCubic: function (x, t, b, c, d) {
10853
+ return c*((t=t/d-1)*t*t + 1) + b;
10854
+ },
10855
+ easeInOutCubic: function (x, t, b, c, d) {
10856
+ if ((t/=d/2) < 1) return c/2*t*t*t + b;
10857
+ return c/2*((t-=2)*t*t + 2) + b;
10858
+ },
10859
+ easeInQuart: function (x, t, b, c, d) {
10860
+ return c*(t/=d)*t*t*t + b;
10861
+ },
10862
+ easeOutQuart: function (x, t, b, c, d) {
10863
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
10864
+ },
10865
+ easeInOutQuart: function (x, t, b, c, d) {
10866
+ if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
10867
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
10868
+ },
10869
+ easeInQuint: function (x, t, b, c, d) {
10870
+ return c*(t/=d)*t*t*t*t + b;
10871
+ },
10872
+ easeOutQuint: function (x, t, b, c, d) {
10873
+ return c*((t=t/d-1)*t*t*t*t + 1) + b;
10874
+ },
10875
+ easeInOutQuint: function (x, t, b, c, d) {
10876
+ if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
10877
+ return c/2*((t-=2)*t*t*t*t + 2) + b;
10878
+ },
10879
+ easeInSine: function (x, t, b, c, d) {
10880
+ return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
10881
+ },
10882
+ easeOutSine: function (x, t, b, c, d) {
10883
+ return c * Math.sin(t/d * (Math.PI/2)) + b;
10884
+ },
10885
+ easeInOutSine: function (x, t, b, c, d) {
10886
+ return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
10887
+ },
10888
+ easeInExpo: function (x, t, b, c, d) {
10889
+ return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
10890
+ },
10891
+ easeOutExpo: function (x, t, b, c, d) {
10892
+ return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
10893
+ },
10894
+ easeInOutExpo: function (x, t, b, c, d) {
10895
+ if (t==0) return b;
10896
+ if (t==d) return b+c;
10897
+ if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
10898
+ return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
10899
+ },
10900
+ easeInCirc: function (x, t, b, c, d) {
10901
+ return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
10902
+ },
10903
+ easeOutCirc: function (x, t, b, c, d) {
10904
+ return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
10905
+ },
10906
+ easeInOutCirc: function (x, t, b, c, d) {
10907
+ if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
10908
+ return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
10909
+ },
10910
+ easeInElastic: function (x, t, b, c, d) {
10911
+ var s=1.70158;var p=0;var a=c;
10912
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
10913
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
10914
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
10915
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
10916
+ },
10917
+ easeOutElastic: function (x, t, b, c, d) {
10918
+ var s=1.70158;var p=0;var a=c;
10919
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
10920
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
10921
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
10922
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
10923
+ },
10924
+ easeInOutElastic: function (x, t, b, c, d) {
10925
+ var s=1.70158;var p=0;var a=c;
10926
+ if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
10927
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
10928
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
10929
+ if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
10930
+ return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
10931
+ },
10932
+ easeInBack: function (x, t, b, c, d, s) {
10933
+ if (s == undefined) s = 1.70158;
10934
+ return c*(t/=d)*t*((s+1)*t - s) + b;
10935
+ },
10936
+ easeOutBack: function (x, t, b, c, d, s) {
10937
+ if (s == undefined) s = 1.70158;
10938
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
10939
+ },
10940
+ easeInOutBack: function (x, t, b, c, d, s) {
10941
+ if (s == undefined) s = 1.70158;
10942
+ if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
10943
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
10944
+ },
10945
+ easeInBounce: function (x, t, b, c, d) {
10946
+ return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b;
10947
+ },
10948
+ easeOutBounce: function (x, t, b, c, d) {
10949
+ if ((t/=d) < (1/2.75)) {
10950
+ return c*(7.5625*t*t) + b;
10951
+ } else if (t < (2/2.75)) {
10952
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
10953
+ } else if (t < (2.5/2.75)) {
10954
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
10955
+ } else {
10956
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
10957
+ }
10958
+ },
10959
+ easeInOutBounce: function (x, t, b, c, d) {
10960
+ if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
10961
+ return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
10962
+ }
10963
+ });
10964
+
10965
+ /*
10966
+ *
10967
+ * TERMS OF USE - EASING EQUATIONS
10968
+ *
10969
+ * Open source under the BSD License.
10970
+ *
10971
+ * Copyright 2001 Robert Penner
10972
+ * All rights reserved.
10973
+ *
10974
+ * Redistribution and use in source and binary forms, with or without modification,
10975
+ * are permitted provided that the following conditions are met:
10976
+ *
10977
+ * Redistributions of source code must retain the above copyright notice, this list of
10978
+ * conditions and the following disclaimer.
10979
+ * Redistributions in binary form must reproduce the above copyright notice, this list
10980
+ * of conditions and the following disclaimer in the documentation and/or other materials
10981
+ * provided with the distribution.
10982
+ *
10983
+ * Neither the name of the author nor the names of contributors may be used to endorse
10984
+ * or promote products derived from this software without specific prior written permission.
10985
+ *
10986
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
10987
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
10988
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
10989
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
10990
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
10991
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
10992
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
10993
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
10994
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
10995
+ *
10996
+ */
10997
+
10998
+ })(jQuery);
10999
+ /*
11000
+ * jQuery UI Effects Blind 1.8.17
11001
+ *
11002
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11003
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11004
+ * http://jquery.org/license
11005
+ *
11006
+ * http://docs.jquery.com/UI/Effects/Blind
11007
+ *
11008
+ * Depends:
11009
+ * jquery.effects.core.js
11010
+ */
11011
+ (function( $, undefined ) {
11012
+
11013
+ $.effects.blind = function(o) {
11014
+
11015
+ return this.queue(function() {
11016
+
11017
+ // Create element
11018
+ var el = $(this), props = ['position','top','bottom','left','right'];
11019
+
11020
+ // Set options
11021
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
11022
+ var direction = o.options.direction || 'vertical'; // Default direction
11023
+
11024
+ // Adjust
11025
+ $.effects.save(el, props); el.show(); // Save & Show
11026
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
11027
+ var ref = (direction == 'vertical') ? 'height' : 'width';
11028
+ var distance = (direction == 'vertical') ? wrapper.height() : wrapper.width();
11029
+ if(mode == 'show') wrapper.css(ref, 0); // Shift
11030
+
11031
+ // Animation
11032
+ var animation = {};
11033
+ animation[ref] = mode == 'show' ? distance : 0;
11034
+
11035
+ // Animate
11036
+ wrapper.animate(animation, o.duration, o.options.easing, function() {
11037
+ if(mode == 'hide') el.hide(); // Hide
11038
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
11039
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
11040
+ el.dequeue();
11041
+ });
11042
+
11043
+ });
11044
+
11045
+ };
11046
+
11047
+ })(jQuery);
11048
+ /*
11049
+ * jQuery UI Effects Bounce 1.8.17
11050
+ *
11051
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11052
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11053
+ * http://jquery.org/license
11054
+ *
11055
+ * http://docs.jquery.com/UI/Effects/Bounce
11056
+ *
11057
+ * Depends:
11058
+ * jquery.effects.core.js
11059
+ */
11060
+ (function( $, undefined ) {
11061
+
11062
+ $.effects.bounce = function(o) {
11063
+
11064
+ return this.queue(function() {
11065
+
11066
+ // Create element
11067
+ var el = $(this), props = ['position','top','bottom','left','right'];
11068
+
11069
+ // Set options
11070
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
11071
+ var direction = o.options.direction || 'up'; // Default direction
11072
+ var distance = o.options.distance || 20; // Default distance
11073
+ var times = o.options.times || 5; // Default # of times
11074
+ var speed = o.duration || 250; // Default speed per bounce
11075
+ if (/show|hide/.test(mode)) props.push('opacity'); // Avoid touching opacity to prevent clearType and PNG issues in IE
11076
+
11077
+ // Adjust
11078
+ $.effects.save(el, props); el.show(); // Save & Show
11079
+ $.effects.createWrapper(el); // Create Wrapper
11080
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
11081
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
11082
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 3 : el.outerWidth({margin:true}) / 3);
11083
+ if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
11084
+ if (mode == 'hide') distance = distance / (times * 2);
11085
+ if (mode != 'hide') times--;
11086
+
11087
+ // Animate
11088
+ if (mode == 'show') { // Show Bounce
11089
+ var animation = {opacity: 1};
11090
+ animation[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
11091
+ el.animate(animation, speed / 2, o.options.easing);
11092
+ distance = distance / 2;
11093
+ times--;
11094
+ };
11095
+ for (var i = 0; i < times; i++) { // Bounces
11096
+ var animation1 = {}, animation2 = {};
11097
+ animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
11098
+ animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
11099
+ el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing);
11100
+ distance = (mode == 'hide') ? distance * 2 : distance / 2;
11101
+ };
11102
+ if (mode == 'hide') { // Last Bounce
11103
+ var animation = {opacity: 0};
11104
+ animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
11105
+ el.animate(animation, speed / 2, o.options.easing, function(){
11106
+ el.hide(); // Hide
11107
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
11108
+ if(o.callback) o.callback.apply(this, arguments); // Callback
11109
+ });
11110
+ } else {
11111
+ var animation1 = {}, animation2 = {};
11112
+ animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
11113
+ animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
11114
+ el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing, function(){
11115
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
11116
+ if(o.callback) o.callback.apply(this, arguments); // Callback
11117
+ });
11118
+ };
11119
+ el.queue('fx', function() { el.dequeue(); });
11120
+ el.dequeue();
11121
+ });
11122
+
11123
+ };
11124
+
11125
+ })(jQuery);
11126
+ /*
11127
+ * jQuery UI Effects Clip 1.8.17
11128
+ *
11129
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11130
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11131
+ * http://jquery.org/license
11132
+ *
11133
+ * http://docs.jquery.com/UI/Effects/Clip
11134
+ *
11135
+ * Depends:
11136
+ * jquery.effects.core.js
11137
+ */
11138
+ (function( $, undefined ) {
11139
+
11140
+ $.effects.clip = function(o) {
11141
+
11142
+ return this.queue(function() {
11143
+
11144
+ // Create element
11145
+ var el = $(this), props = ['position','top','bottom','left','right','height','width'];
11146
+
11147
+ // Set options
11148
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
11149
+ var direction = o.options.direction || 'vertical'; // Default direction
11150
+
11151
+ // Adjust
11152
+ $.effects.save(el, props); el.show(); // Save & Show
11153
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
11154
+ var animate = el[0].tagName == 'IMG' ? wrapper : el;
11155
+ var ref = {
11156
+ size: (direction == 'vertical') ? 'height' : 'width',
11157
+ position: (direction == 'vertical') ? 'top' : 'left'
11158
+ };
11159
+ var distance = (direction == 'vertical') ? animate.height() : animate.width();
11160
+ if(mode == 'show') { animate.css(ref.size, 0); animate.css(ref.position, distance / 2); } // Shift
11161
+
11162
+ // Animation
11163
+ var animation = {};
11164
+ animation[ref.size] = mode == 'show' ? distance : 0;
11165
+ animation[ref.position] = mode == 'show' ? 0 : distance / 2;
11166
+
11167
+ // Animate
11168
+ animate.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
11169
+ if(mode == 'hide') el.hide(); // Hide
11170
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
11171
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
11172
+ el.dequeue();
11173
+ }});
11174
+
11175
+ });
11176
+
11177
+ };
11178
+
11179
+ })(jQuery);
11180
+ /*
11181
+ * jQuery UI Effects Drop 1.8.17
11182
+ *
11183
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11184
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11185
+ * http://jquery.org/license
11186
+ *
11187
+ * http://docs.jquery.com/UI/Effects/Drop
11188
+ *
11189
+ * Depends:
11190
+ * jquery.effects.core.js
11191
+ */
11192
+ (function( $, undefined ) {
11193
+
11194
+ $.effects.drop = function(o) {
11195
+
11196
+ return this.queue(function() {
11197
+
11198
+ // Create element
11199
+ var el = $(this), props = ['position','top','bottom','left','right','opacity'];
11200
+
11201
+ // Set options
11202
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
11203
+ var direction = o.options.direction || 'left'; // Default Direction
11204
+
11205
+ // Adjust
11206
+ $.effects.save(el, props); el.show(); // Save & Show
11207
+ $.effects.createWrapper(el); // Create Wrapper
11208
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
11209
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
11210
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 2 : el.outerWidth({margin:true}) / 2);
11211
+ if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
11212
+
11213
+ // Animation
11214
+ var animation = {opacity: mode == 'show' ? 1 : 0};
11215
+ animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
11216
+
11217
+ // Animate
11218
+ el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
11219
+ if(mode == 'hide') el.hide(); // Hide
11220
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
11221
+ if(o.callback) o.callback.apply(this, arguments); // Callback
11222
+ el.dequeue();
11223
+ }});
11224
+
11225
+ });
11226
+
11227
+ };
11228
+
11229
+ })(jQuery);
11230
+ /*
11231
+ * jQuery UI Effects Explode 1.8.17
11232
+ *
11233
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11234
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11235
+ * http://jquery.org/license
11236
+ *
11237
+ * http://docs.jquery.com/UI/Effects/Explode
11238
+ *
11239
+ * Depends:
11240
+ * jquery.effects.core.js
11241
+ */
11242
+ (function( $, undefined ) {
11243
+
11244
+ $.effects.explode = function(o) {
11245
+
11246
+ return this.queue(function() {
11247
+
11248
+ var rows = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
11249
+ var cells = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
11250
+
11251
+ o.options.mode = o.options.mode == 'toggle' ? ($(this).is(':visible') ? 'hide' : 'show') : o.options.mode;
11252
+ var el = $(this).show().css('visibility', 'hidden');
11253
+ var offset = el.offset();
11254
+
11255
+ //Substract the margins - not fixing the problem yet.
11256
+ offset.top -= parseInt(el.css("marginTop"),10) || 0;
11257
+ offset.left -= parseInt(el.css("marginLeft"),10) || 0;
11258
+
11259
+ var width = el.outerWidth(true);
11260
+ var height = el.outerHeight(true);
11261
+
11262
+ for(var i=0;i<rows;i++) { // =
11263
+ for(var j=0;j<cells;j++) { // ||
11264
+ el
11265
+ .clone()
11266
+ .appendTo('body')
11267
+ .wrap('<div></div>')
11268
+ .css({
11269
+ position: 'absolute',
11270
+ visibility: 'visible',
11271
+ left: -j*(width/cells),
11272
+ top: -i*(height/rows)
11273
+ })
11274
+ .parent()
11275
+ .addClass('ui-effects-explode')
11276
+ .css({
11277
+ position: 'absolute',
11278
+ overflow: 'hidden',
11279
+ width: width/cells,
11280
+ height: height/rows,
11281
+ left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? (j-Math.floor(cells/2))*(width/cells) : 0),
11282
+ top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? (i-Math.floor(rows/2))*(height/rows) : 0),
11283
+ opacity: o.options.mode == 'show' ? 0 : 1
11284
+ }).animate({
11285
+ left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? 0 : (j-Math.floor(cells/2))*(width/cells)),
11286
+ top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? 0 : (i-Math.floor(rows/2))*(height/rows)),
11287
+ opacity: o.options.mode == 'show' ? 1 : 0
11288
+ }, o.duration || 500);
11289
+ }
11290
+ }
11291
+
11292
+ // Set a timeout, to call the callback approx. when the other animations have finished
11293
+ setTimeout(function() {
11294
+
11295
+ o.options.mode == 'show' ? el.css({ visibility: 'visible' }) : el.css({ visibility: 'visible' }).hide();
11296
+ if(o.callback) o.callback.apply(el[0]); // Callback
11297
+ el.dequeue();
11298
+
11299
+ $('div.ui-effects-explode').remove();
11300
+
11301
+ }, o.duration || 500);
11302
+
11303
+
11304
+ });
11305
+
11306
+ };
11307
+
11308
+ })(jQuery);
11309
+ /*
11310
+ * jQuery UI Effects Fade 1.8.17
11311
+ *
11312
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11313
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11314
+ * http://jquery.org/license
11315
+ *
11316
+ * http://docs.jquery.com/UI/Effects/Fade
11317
+ *
11318
+ * Depends:
11319
+ * jquery.effects.core.js
11320
+ */
11321
+ (function( $, undefined ) {
11322
+
11323
+ $.effects.fade = function(o) {
11324
+ return this.queue(function() {
11325
+ var elem = $(this),
11326
+ mode = $.effects.setMode(elem, o.options.mode || 'hide');
11327
+
11328
+ elem.animate({ opacity: mode }, {
11329
+ queue: false,
11330
+ duration: o.duration,
11331
+ easing: o.options.easing,
11332
+ complete: function() {
11333
+ (o.callback && o.callback.apply(this, arguments));
11334
+ elem.dequeue();
11335
+ }
11336
+ });
11337
+ });
11338
+ };
11339
+
11340
+ })(jQuery);
11341
+ /*
11342
+ * jQuery UI Effects Fold 1.8.17
11343
+ *
11344
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11345
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11346
+ * http://jquery.org/license
11347
+ *
11348
+ * http://docs.jquery.com/UI/Effects/Fold
11349
+ *
11350
+ * Depends:
11351
+ * jquery.effects.core.js
11352
+ */
11353
+ (function( $, undefined ) {
11354
+
11355
+ $.effects.fold = function(o) {
11356
+
11357
+ return this.queue(function() {
11358
+
11359
+ // Create element
11360
+ var el = $(this), props = ['position','top','bottom','left','right'];
11361
+
11362
+ // Set options
11363
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
11364
+ var size = o.options.size || 15; // Default fold size
11365
+ var horizFirst = !(!o.options.horizFirst); // Ensure a boolean value
11366
+ var duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2;
11367
+
11368
+ // Adjust
11369
+ $.effects.save(el, props); el.show(); // Save & Show
11370
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
11371
+ var widthFirst = ((mode == 'show') != horizFirst);
11372
+ var ref = widthFirst ? ['width', 'height'] : ['height', 'width'];
11373
+ var distance = widthFirst ? [wrapper.width(), wrapper.height()] : [wrapper.height(), wrapper.width()];
11374
+ var percent = /([0-9]+)%/.exec(size);
11375
+ if(percent) size = parseInt(percent[1],10) / 100 * distance[mode == 'hide' ? 0 : 1];
11376
+ if(mode == 'show') wrapper.css(horizFirst ? {height: 0, width: size} : {height: size, width: 0}); // Shift
11377
+
11378
+ // Animation
11379
+ var animation1 = {}, animation2 = {};
11380
+ animation1[ref[0]] = mode == 'show' ? distance[0] : size;
11381
+ animation2[ref[1]] = mode == 'show' ? distance[1] : 0;
11382
+
11383
+ // Animate
11384
+ wrapper.animate(animation1, duration, o.options.easing)
11385
+ .animate(animation2, duration, o.options.easing, function() {
11386
+ if(mode == 'hide') el.hide(); // Hide
11387
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
11388
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
11389
+ el.dequeue();
11390
+ });
11391
+
11392
+ });
11393
+
11394
+ };
11395
+
11396
+ })(jQuery);
11397
+ /*
11398
+ * jQuery UI Effects Highlight 1.8.17
11399
+ *
11400
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11401
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11402
+ * http://jquery.org/license
11403
+ *
11404
+ * http://docs.jquery.com/UI/Effects/Highlight
11405
+ *
11406
+ * Depends:
11407
+ * jquery.effects.core.js
11408
+ */
11409
+ (function( $, undefined ) {
11410
+
11411
+ $.effects.highlight = function(o) {
11412
+ return this.queue(function() {
11413
+ var elem = $(this),
11414
+ props = ['backgroundImage', 'backgroundColor', 'opacity'],
11415
+ mode = $.effects.setMode(elem, o.options.mode || 'show'),
11416
+ animation = {
11417
+ backgroundColor: elem.css('backgroundColor')
11418
+ };
11419
+
11420
+ if (mode == 'hide') {
11421
+ animation.opacity = 0;
11422
+ }
11423
+
11424
+ $.effects.save(elem, props);
11425
+ elem
11426
+ .show()
11427
+ .css({
11428
+ backgroundImage: 'none',
11429
+ backgroundColor: o.options.color || '#ffff99'
11430
+ })
11431
+ .animate(animation, {
11432
+ queue: false,
11433
+ duration: o.duration,
11434
+ easing: o.options.easing,
11435
+ complete: function() {
11436
+ (mode == 'hide' && elem.hide());
11437
+ $.effects.restore(elem, props);
11438
+ (mode == 'show' && !$.support.opacity && this.style.removeAttribute('filter'));
11439
+ (o.callback && o.callback.apply(this, arguments));
11440
+ elem.dequeue();
11441
+ }
11442
+ });
11443
+ });
11444
+ };
11445
+
11446
+ })(jQuery);
11447
+ /*
11448
+ * jQuery UI Effects Pulsate 1.8.17
11449
+ *
11450
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11451
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11452
+ * http://jquery.org/license
11453
+ *
11454
+ * http://docs.jquery.com/UI/Effects/Pulsate
11455
+ *
11456
+ * Depends:
11457
+ * jquery.effects.core.js
11458
+ */
11459
+ (function( $, undefined ) {
11460
+
11461
+ $.effects.pulsate = function(o) {
11462
+ return this.queue(function() {
11463
+ var elem = $(this),
11464
+ mode = $.effects.setMode(elem, o.options.mode || 'show');
11465
+ times = ((o.options.times || 5) * 2) - 1;
11466
+ duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2,
11467
+ isVisible = elem.is(':visible'),
11468
+ animateTo = 0;
11469
+
11470
+ if (!isVisible) {
11471
+ elem.css('opacity', 0).show();
11472
+ animateTo = 1;
11473
+ }
11474
+
11475
+ if ((mode == 'hide' && isVisible) || (mode == 'show' && !isVisible)) {
11476
+ times--;
11477
+ }
11478
+
11479
+ for (var i = 0; i < times; i++) {
11480
+ elem.animate({ opacity: animateTo }, duration, o.options.easing);
11481
+ animateTo = (animateTo + 1) % 2;
11482
+ }
11483
+
11484
+ elem.animate({ opacity: animateTo }, duration, o.options.easing, function() {
11485
+ if (animateTo == 0) {
11486
+ elem.hide();
11487
+ }
11488
+ (o.callback && o.callback.apply(this, arguments));
11489
+ });
11490
+
11491
+ elem
11492
+ .queue('fx', function() { elem.dequeue(); })
11493
+ .dequeue();
11494
+ });
11495
+ };
11496
+
11497
+ })(jQuery);
11498
+ /*
11499
+ * jQuery UI Effects Scale 1.8.17
11500
+ *
11501
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11502
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11503
+ * http://jquery.org/license
11504
+ *
11505
+ * http://docs.jquery.com/UI/Effects/Scale
11506
+ *
11507
+ * Depends:
11508
+ * jquery.effects.core.js
11509
+ */
11510
+ (function( $, undefined ) {
11511
+
11512
+ $.effects.puff = function(o) {
11513
+ return this.queue(function() {
11514
+ var elem = $(this),
11515
+ mode = $.effects.setMode(elem, o.options.mode || 'hide'),
11516
+ percent = parseInt(o.options.percent, 10) || 150,
11517
+ factor = percent / 100,
11518
+ original = { height: elem.height(), width: elem.width() };
11519
+
11520
+ $.extend(o.options, {
11521
+ fade: true,
11522
+ mode: mode,
11523
+ percent: mode == 'hide' ? percent : 100,
11524
+ from: mode == 'hide'
11525
+ ? original
11526
+ : {
11527
+ height: original.height * factor,
11528
+ width: original.width * factor
11529
+ }
11530
+ });
11531
+
11532
+ elem.effect('scale', o.options, o.duration, o.callback);
11533
+ elem.dequeue();
11534
+ });
11535
+ };
11536
+
11537
+ $.effects.scale = function(o) {
11538
+
11539
+ return this.queue(function() {
11540
+
11541
+ // Create element
11542
+ var el = $(this);
11543
+
11544
+ // Set options
11545
+ var options = $.extend(true, {}, o.options);
11546
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
11547
+ var percent = parseInt(o.options.percent,10) || (parseInt(o.options.percent,10) == 0 ? 0 : (mode == 'hide' ? 0 : 100)); // Set default scaling percent
11548
+ var direction = o.options.direction || 'both'; // Set default axis
11549
+ var origin = o.options.origin; // The origin of the scaling
11550
+ if (mode != 'effect') { // Set default origin and restore for show/hide
11551
+ options.origin = origin || ['middle','center'];
11552
+ options.restore = true;
11553
+ }
11554
+ var original = {height: el.height(), width: el.width()}; // Save original
11555
+ el.from = o.options.from || (mode == 'show' ? {height: 0, width: 0} : original); // Default from state
11556
+
11557
+ // Adjust
11558
+ var factor = { // Set scaling factor
11559
+ y: direction != 'horizontal' ? (percent / 100) : 1,
11560
+ x: direction != 'vertical' ? (percent / 100) : 1
11561
+ };
11562
+ el.to = {height: original.height * factor.y, width: original.width * factor.x}; // Set to state
11563
+
11564
+ if (o.options.fade) { // Fade option to support puff
11565
+ if (mode == 'show') {el.from.opacity = 0; el.to.opacity = 1;};
11566
+ if (mode == 'hide') {el.from.opacity = 1; el.to.opacity = 0;};
11567
+ };
11568
+
11569
+ // Animation
11570
+ options.from = el.from; options.to = el.to; options.mode = mode;
11571
+
11572
+ // Animate
11573
+ el.effect('size', options, o.duration, o.callback);
11574
+ el.dequeue();
11575
+ });
11576
+
11577
+ };
11578
+
11579
+ $.effects.size = function(o) {
11580
+
11581
+ return this.queue(function() {
11582
+
11583
+ // Create element
11584
+ var el = $(this), props = ['position','top','bottom','left','right','width','height','overflow','opacity'];
11585
+ var props1 = ['position','top','bottom','left','right','overflow','opacity']; // Always restore
11586
+ var props2 = ['width','height','overflow']; // Copy for children
11587
+ var cProps = ['fontSize'];
11588
+ var vProps = ['borderTopWidth', 'borderBottomWidth', 'paddingTop', 'paddingBottom'];
11589
+ var hProps = ['borderLeftWidth', 'borderRightWidth', 'paddingLeft', 'paddingRight'];
11590
+
11591
+ // Set options
11592
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
11593
+ var restore = o.options.restore || false; // Default restore
11594
+ var scale = o.options.scale || 'both'; // Default scale mode
11595
+ var origin = o.options.origin; // The origin of the sizing
11596
+ var original = {height: el.height(), width: el.width()}; // Save original
11597
+ el.from = o.options.from || original; // Default from state
11598
+ el.to = o.options.to || original; // Default to state
11599
+ // Adjust
11600
+ if (origin) { // Calculate baseline shifts
11601
+ var baseline = $.effects.getBaseline(origin, original);
11602
+ el.from.top = (original.height - el.from.height) * baseline.y;
11603
+ el.from.left = (original.width - el.from.width) * baseline.x;
11604
+ el.to.top = (original.height - el.to.height) * baseline.y;
11605
+ el.to.left = (original.width - el.to.width) * baseline.x;
11606
+ };
11607
+ var factor = { // Set scaling factor
11608
+ from: {y: el.from.height / original.height, x: el.from.width / original.width},
11609
+ to: {y: el.to.height / original.height, x: el.to.width / original.width}
11610
+ };
11611
+ if (scale == 'box' || scale == 'both') { // Scale the css box
11612
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
11613
+ props = props.concat(vProps);
11614
+ el.from = $.effects.setTransition(el, vProps, factor.from.y, el.from);
11615
+ el.to = $.effects.setTransition(el, vProps, factor.to.y, el.to);
11616
+ };
11617
+ if (factor.from.x != factor.to.x) { // Horizontal props scaling
11618
+ props = props.concat(hProps);
11619
+ el.from = $.effects.setTransition(el, hProps, factor.from.x, el.from);
11620
+ el.to = $.effects.setTransition(el, hProps, factor.to.x, el.to);
11621
+ };
11622
+ };
11623
+ if (scale == 'content' || scale == 'both') { // Scale the content
11624
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
11625
+ props = props.concat(cProps);
11626
+ el.from = $.effects.setTransition(el, cProps, factor.from.y, el.from);
11627
+ el.to = $.effects.setTransition(el, cProps, factor.to.y, el.to);
11628
+ };
11629
+ };
11630
+ $.effects.save(el, restore ? props : props1); el.show(); // Save & Show
11631
+ $.effects.createWrapper(el); // Create Wrapper
11632
+ el.css('overflow','hidden').css(el.from); // Shift
11633
+
11634
+ // Animate
11635
+ if (scale == 'content' || scale == 'both') { // Scale the children
11636
+ vProps = vProps.concat(['marginTop','marginBottom']).concat(cProps); // Add margins/font-size
11637
+ hProps = hProps.concat(['marginLeft','marginRight']); // Add margins
11638
+ props2 = props.concat(vProps).concat(hProps); // Concat
11639
+ el.find("*[width]").each(function(){
11640
+ child = $(this);
11641
+ if (restore) $.effects.save(child, props2);
11642
+ var c_original = {height: child.height(), width: child.width()}; // Save original
11643
+ child.from = {height: c_original.height * factor.from.y, width: c_original.width * factor.from.x};
11644
+ child.to = {height: c_original.height * factor.to.y, width: c_original.width * factor.to.x};
11645
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
11646
+ child.from = $.effects.setTransition(child, vProps, factor.from.y, child.from);
11647
+ child.to = $.effects.setTransition(child, vProps, factor.to.y, child.to);
11648
+ };
11649
+ if (factor.from.x != factor.to.x) { // Horizontal props scaling
11650
+ child.from = $.effects.setTransition(child, hProps, factor.from.x, child.from);
11651
+ child.to = $.effects.setTransition(child, hProps, factor.to.x, child.to);
11652
+ };
11653
+ child.css(child.from); // Shift children
11654
+ child.animate(child.to, o.duration, o.options.easing, function(){
11655
+ if (restore) $.effects.restore(child, props2); // Restore children
11656
+ }); // Animate children
11657
+ });
11658
+ };
11659
+
11660
+ // Animate
11661
+ el.animate(el.to, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
11662
+ if (el.to.opacity === 0) {
11663
+ el.css('opacity', el.from.opacity);
11664
+ }
11665
+ if(mode == 'hide') el.hide(); // Hide
11666
+ $.effects.restore(el, restore ? props : props1); $.effects.removeWrapper(el); // Restore
11667
+ if(o.callback) o.callback.apply(this, arguments); // Callback
11668
+ el.dequeue();
11669
+ }});
11670
+
11671
+ });
11672
+
11673
+ };
11674
+
11675
+ })(jQuery);
11676
+ /*
11677
+ * jQuery UI Effects Shake 1.8.17
11678
+ *
11679
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11680
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11681
+ * http://jquery.org/license
11682
+ *
11683
+ * http://docs.jquery.com/UI/Effects/Shake
11684
+ *
11685
+ * Depends:
11686
+ * jquery.effects.core.js
11687
+ */
11688
+ (function( $, undefined ) {
11689
+
11690
+ $.effects.shake = function(o) {
11691
+
11692
+ return this.queue(function() {
11693
+
11694
+ // Create element
11695
+ var el = $(this), props = ['position','top','bottom','left','right'];
11696
+
11697
+ // Set options
11698
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
11699
+ var direction = o.options.direction || 'left'; // Default direction
11700
+ var distance = o.options.distance || 20; // Default distance
11701
+ var times = o.options.times || 3; // Default # of times
11702
+ var speed = o.duration || o.options.duration || 140; // Default speed per shake
11703
+
11704
+ // Adjust
11705
+ $.effects.save(el, props); el.show(); // Save & Show
11706
+ $.effects.createWrapper(el); // Create Wrapper
11707
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
11708
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
11709
+
11710
+ // Animation
11711
+ var animation = {}, animation1 = {}, animation2 = {};
11712
+ animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
11713
+ animation1[ref] = (motion == 'pos' ? '+=' : '-=') + distance * 2;
11714
+ animation2[ref] = (motion == 'pos' ? '-=' : '+=') + distance * 2;
11715
+
11716
+ // Animate
11717
+ el.animate(animation, speed, o.options.easing);
11718
+ for (var i = 1; i < times; i++) { // Shakes
11719
+ el.animate(animation1, speed, o.options.easing).animate(animation2, speed, o.options.easing);
11720
+ };
11721
+ el.animate(animation1, speed, o.options.easing).
11722
+ animate(animation, speed / 2, o.options.easing, function(){ // Last shake
11723
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
11724
+ if(o.callback) o.callback.apply(this, arguments); // Callback
11725
+ });
11726
+ el.queue('fx', function() { el.dequeue(); });
11727
+ el.dequeue();
11728
+ });
11729
+
11730
+ };
11731
+
11732
+ })(jQuery);
11733
+ /*
11734
+ * jQuery UI Effects Slide 1.8.17
11735
+ *
11736
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11737
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11738
+ * http://jquery.org/license
11739
+ *
11740
+ * http://docs.jquery.com/UI/Effects/Slide
11741
+ *
11742
+ * Depends:
11743
+ * jquery.effects.core.js
11744
+ */
11745
+ (function( $, undefined ) {
11746
+
11747
+ $.effects.slide = function(o) {
11748
+
11749
+ return this.queue(function() {
11750
+
11751
+ // Create element
11752
+ var el = $(this), props = ['position','top','bottom','left','right'];
11753
+
11754
+ // Set options
11755
+ var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
11756
+ var direction = o.options.direction || 'left'; // Default Direction
11757
+
11758
+ // Adjust
11759
+ $.effects.save(el, props); el.show(); // Save & Show
11760
+ $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
11761
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
11762
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
11763
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) : el.outerWidth({margin:true}));
11764
+ if (mode == 'show') el.css(ref, motion == 'pos' ? (isNaN(distance) ? "-" + distance : -distance) : distance); // Shift
11765
+
11766
+ // Animation
11767
+ var animation = {};
11768
+ animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
11769
+
11770
+ // Animate
11771
+ el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
11772
+ if(mode == 'hide') el.hide(); // Hide
11773
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
11774
+ if(o.callback) o.callback.apply(this, arguments); // Callback
11775
+ el.dequeue();
11776
+ }});
11777
+
11778
+ });
11779
+
11780
+ };
11781
+
11782
+ })(jQuery);
11783
+ /*
11784
+ * jQuery UI Effects Transfer 1.8.17
11785
+ *
11786
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
11787
+ * Dual licensed under the MIT or GPL Version 2 licenses.
11788
+ * http://jquery.org/license
11789
+ *
11790
+ * http://docs.jquery.com/UI/Effects/Transfer
11791
+ *
11792
+ * Depends:
11793
+ * jquery.effects.core.js
11794
+ */
11795
+ (function( $, undefined ) {
11796
+
11797
+ $.effects.transfer = function(o) {
11798
+ return this.queue(function() {
11799
+ var elem = $(this),
11800
+ target = $(o.options.to),
11801
+ endPosition = target.offset(),
11802
+ animation = {
11803
+ top: endPosition.top,
11804
+ left: endPosition.left,
11805
+ height: target.innerHeight(),
11806
+ width: target.innerWidth()
11807
+ },
11808
+ startPosition = elem.offset(),
11809
+ transfer = $('<div class="ui-effects-transfer"></div>')
11810
+ .appendTo(document.body)
11811
+ .addClass(o.options.className)
11812
+ .css({
11813
+ top: startPosition.top,
11814
+ left: startPosition.left,
11815
+ height: elem.innerHeight(),
11816
+ width: elem.innerWidth(),
11817
+ position: 'absolute'
11818
+ })
11819
+ .animate(animation, o.duration, o.options.easing, function() {
11820
+ transfer.remove();
11821
+ (o.callback && o.callback.apply(elem[0], arguments));
11822
+ elem.dequeue();
11823
+ });
11824
+ });
11825
+ };
11826
+
11827
+ })(jQuery);
js/jquery/jquery-ui.min.js ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery UI 1.8.17
3
+ *
4
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://docs.jquery.com/UI
9
+ */(function(a,b){function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;if(!b.href||!g||f.nodeName.toLowerCase()!=="map")return!1;h=a("img[usemap=#"+g+"]")[0];return!!h&&d(h)}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}a.ui=a.ui||{};a.ui.version||(a.extend(a.ui,{version:"1.8.17",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)});return c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){if(c===b)return g["inner"+d].call(this);return this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){if(typeof b!="number")return g["outer"+d].call(this,b);return this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!!d&&!!a.element[0].parentNode)for(var e=0;e<d.length;e++)a.options[d[e][0]]&&d[e][1].apply(a.element,c)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(b,c){if(a(b).css("overflow")==="hidden")return!1;var d=c&&c==="left"?"scrollLeft":"scrollTop",e=!1;if(b[d]>0)return!0;b[d]=1,e=b[d]>0,b[d]=0;return e},isOverAxis:function(a,b,c){return a>b&&a<b+c},isOver:function(b,c,d,e,f,g){return a.ui.isOverAxis(b,d,f)&&a.ui.isOverAxis(c,e,g)}}))})(jQuery);/*!
10
+ * jQuery UI Widget 1.8.17
11
+ *
12
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
13
+ * Dual licensed under the MIT or GPL Version 2 licenses.
14
+ * http://jquery.org/license
15
+ *
16
+ * http://docs.jquery.com/UI/Widget
17
+ */(function(a,b){if(a.cleanData){var c=a.cleanData;a.cleanData=function(b){for(var d=0,e;(e=b[d])!=null;d++)try{a(e).triggerHandler("remove")}catch(f){}c(b)}}else{var d=a.fn.remove;a.fn.remove=function(b,c){return this.each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){try{a(this).triggerHandler("remove")}catch(b){}});return d.call(a(this),b,c)})}}a.widget=function(b,c,d){var e=b.split(".")[0],f;b=b.split(".")[1],f=e+"-"+b,d||(d=c,c=a.Widget),a.expr[":"][f]=function(c){return!!a.data(c,b)},a[e]=a[e]||{},a[e][b]=function(a,b){arguments.length&&this._createWidget(a,b)};var g=new c;g.options=a.extend(!0,{},g.options),a[e][b].prototype=a.extend(!0,g,{namespace:e,widgetName:b,widgetEventPrefix:a[e][b].prototype.widgetEventPrefix||b,widgetBaseClass:f},d),a.widget.bridge(b,a[e][b])},a.widget.bridge=function(c,d){a.fn[c]=function(e){var f=typeof e=="string",g=Array.prototype.slice.call(arguments,1),h=this;e=!f&&g.length?a.extend.apply(null,[!0,e].concat(g)):e;if(f&&e.charAt(0)==="_")return h;f?this.each(function(){var d=a.data(this,c),f=d&&a.isFunction(d[e])?d[e].apply(d,g):d;if(f!==d&&f!==b){h=f;return!1}}):this.each(function(){var b=a.data(this,c);b?b.option(e||{})._init():a.data(this,c,new d(e,this))});return h}},a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)},a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:!1},_createWidget:function(b,c){a.data(c,this.widgetName,this),this.element=a(c),this.options=a.extend(!0,{},this.options,this._getCreateOptions(),b);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()}),this._create(),this._trigger("create"),this._init()},_getCreateOptions:function(){return a.metadata&&a.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName),this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled "+"ui-state-disabled")},widget:function(){return this.element},option:function(c,d){var e=c;if(arguments.length===0)return a.extend({},this.options);if(typeof c=="string"){if(d===b)return this.options[c];e={},e[c]=d}this._setOptions(e);return this},_setOptions:function(b){var c=this;a.each(b,function(a,b){c._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b,a==="disabled"&&this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled"+" "+"ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_trigger:function(b,c,d){var e,f,g=this.options[b];d=d||{},c=a.Event(c),c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase(),c.target=this.element[0],f=c.originalEvent;if(f)for(e in f)e in c||(c[e]=f[e]);this.element.trigger(c,d);return!(a.isFunction(g)&&g.call(this.element[0],c,d)===!1||c.isDefaultPrevented())}}})(jQuery);/*!
18
+ * jQuery UI Mouse 1.8.17
19
+ *
20
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
21
+ * Dual licensed under the MIT or GPL Version 2 licenses.
22
+ * http://jquery.org/license
23
+ *
24
+ * http://docs.jquery.com/UI/Mouse
25
+ *
26
+ * Depends:
27
+ * jquery.ui.widget.js
28
+ */(function(a,b){var c=!1;a(document).mouseup(function(a){c=!1}),a.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var b=this;this.element.bind("mousedown."+this.widgetName,function(a){return b._mouseDown(a)}).bind("click."+this.widgetName,function(c){if(!0===a.data(c.target,b.widgetName+".preventClickEvent")){a.removeData(c.target,b.widgetName+".preventClickEvent"),c.stopImmediatePropagation();return!1}}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(b){if(!c){this._mouseStarted&&this._mouseUp(b),this._mouseDownEvent=b;var d=this,e=b.which==1,f=typeof this.options.cancel=="string"&&b.target.nodeName?a(b.target).closest(this.options.cancel).length:!1;if(!e||f||!this._mouseCapture(b))return!0;this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){d.mouseDelayMet=!0},this.options.delay));if(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)){this._mouseStarted=this._mouseStart(b)!==!1;if(!this._mouseStarted){b.preventDefault();return!0}}!0===a.data(b.target,this.widgetName+".preventClickEvent")&&a.removeData(b.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(a){return d._mouseMove(a)},this._mouseUpDelegate=function(a){return d._mouseUp(a)},a(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),b.preventDefault(),c=!0;return!0}},_mouseMove:function(b){if(a.browser.msie&&!(document.documentMode>=9)&&!b.button)return this._mouseUp(b);if(this._mouseStarted){this._mouseDrag(b);return b.preventDefault()}this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b));return!this._mouseStarted},_mouseUp:function(b){a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b));return!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);/*
29
+ * jQuery UI Position 1.8.17
30
+ *
31
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
32
+ * Dual licensed under the MIT or GPL Version 2 licenses.
33
+ * http://jquery.org/license
34
+ *
35
+ * http://docs.jquery.com/UI/Position
36
+ */(function(a,b){a.ui=a.ui||{};var c=/left|center|right/,d=/top|center|bottom/,e="center",f={},g=a.fn.position,h=a.fn.offset;a.fn.position=function(b){if(!b||!b.of)return g.apply(this,arguments);b=a.extend({},b);var h=a(b.of),i=h[0],j=(b.collision||"flip").split(" "),k=b.offset?b.offset.split(" "):[0,0],l,m,n;i.nodeType===9?(l=h.width(),m=h.height(),n={top:0,left:0}):i.setTimeout?(l=h.width(),m=h.height(),n={top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at="left top",l=m=0,n={top:b.of.pageY,left:b.of.pageX}):(l=h.outerWidth(),m=h.outerHeight(),n=h.offset()),a.each(["my","at"],function(){var a=(b[this]||"").split(" ");a.length===1&&(a=c.test(a[0])?a.concat([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=c.test(a[0])?a[0]:e,a[1]=d.test(a[1])?a[1]:e,b[this]=a}),j.length===1&&(j[1]=j[0]),k[0]=parseInt(k[0],10)||0,k.length===1&&(k[1]=k[0]),k[1]=parseInt(k[1],10)||0,b.at[0]==="right"?n.left+=l:b.at[0]===e&&(n.left+=l/2),b.at[1]==="bottom"?n.top+=m:b.at[1]===e&&(n.top+=m/2),n.left+=k[0],n.top+=k[1];return this.each(function(){var c=a(this),d=c.outerWidth(),g=c.outerHeight(),h=parseInt(a.curCSS(this,"marginLeft",!0))||0,i=parseInt(a.curCSS(this,"marginTop",!0))||0,o=d+h+(parseInt(a.curCSS(this,"marginRight",!0))||0),p=g+i+(parseInt(a.curCSS(this,"marginBottom",!0))||0),q=a.extend({},n),r;b.my[0]==="right"?q.left-=d:b.my[0]===e&&(q.left-=d/2),b.my[1]==="bottom"?q.top-=g:b.my[1]===e&&(q.top-=g/2),f.fractions||(q.left=Math.round(q.left),q.top=Math.round(q.top)),r={left:q.left-h,top:q.top-i},a.each(["left","top"],function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth:o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe(),c.offset(a.extend(q,{using:b.using}))})},a.ui.position={fit:{left:function(b,c){var d=a(window),e=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft();b.left=e>0?b.left-e:Math.max(b.left-c.collisionPosition.left,b.left)},top:function(b,c){var d=a(window),e=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=e>0?b.top-e:Math.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c.at[0]!==e){var d=a(window),f=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft(),g=c.my[0]==="left"?-c.elemWidth:c.my[0]==="right"?c.elemWidth:0,h=c.at[0]==="left"?c.targetWidth:-c.targetWidth,i=-2*c.offset[0];b.left+=c.collisionPosition.left<0?g+h+i:f>0?g+h+i:0}},top:function(b,c){if(c.at[1]!==e){var d=a(window),f=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop(),g=c.my[1]==="top"?-c.elemHeight:c.my[1]==="bottom"?c.elemHeight:0,h=c.at[1]==="top"?c.targetHeight:-c.targetHeight,i=-2*c.offset[1];b.top+=c.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}}},a.offset.setOffset||(a.offset.setOffset=function(b,c){/static/.test(a.curCSS(b,"position"))&&(b.style.position="relative");var d=a(b),e=d.offset(),f=parseInt(a.curCSS(b,"top",!0),10)||0,g=parseInt(a.curCSS(b,"left",!0),10)||0,h={top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.call(b,h):d.css(h)},a.fn.offset=function(b){var c=this[0];if(!c||!c.ownerDocument)return null;if(b)return this.each(function(){a.offset.setOffset(this,b)});return h.call(this)}),function(){var b=document.getElementsByTagName("body")[0],c=document.createElement("div"),d,e,g,h,i;d=document.createElement(b?"div":"body"),g={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},b&&jQuery.extend(g,{position:"absolute",left:"-1000px",top:"-1000px"});for(var j in g)d.style[j]=g[j];d.appendChild(c),e=b||document.documentElement,e.insertBefore(d,e.firstChild),c.style.cssText="position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;",h=a(c).offset(function(a,b){return b}).offset(),d.innerHTML="",e.removeChild(d),i=h.top+h.left+(b?2e3:0),f.fractions=i>21&&i<22}()})(jQuery);/*
37
+ * jQuery UI Draggable 1.8.17
38
+ *
39
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
40
+ * Dual licensed under the MIT or GPL Version 2 licenses.
41
+ * http://jquery.org/license
42
+ *
43
+ * http://docs.jquery.com/UI/Draggables
44
+ *
45
+ * Depends:
46
+ * jquery.ui.core.js
47
+ * jquery.ui.mouse.js
48
+ * jquery.ui.widget.js
49
+ */(function(a,b){a.widget("ui.draggable",a.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1},_create:function(){this.options.helper=="original"&&!/^(?:r|a|f)/.test(this.element.css("position"))&&(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},destroy:function(){if(!!this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy();return this}},_mouseCapture:function(b){var c=this.options;if(this.helper||c.disabled||a(b.target).is(".ui-resizable-handle"))return!1;this.handle=this._getHandle(b);if(!this.handle)return!1;c.iframeFix&&a(c.iframeFix===!0?"iframe":c.iframeFix).each(function(){a('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(a(this).offset()).appendTo("body")});return!0},_mouseStart:function(b){var c=this.options;this.helper=this._createHelper(b),this._cacheHelperProportions(),a.ui.ddmanager&&(a.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,c.cursorAt&&this._adjustOffsetFromHelper(c.cursorAt),c.containment&&this._setContainment();if(this._trigger("start",b)===!1){this._clear();return!1}this._cacheHelperProportions(),a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.helper.addClass("ui-draggable-dragging"),this._mouseDrag(b,!0),a.ui.ddmanager&&a.ui.ddmanager.dragStart(this,b);return!0},_mouseDrag:function(b,c){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute");if(!c){var d=this._uiHash();if(this._trigger("drag",b,d)===!1){this._mouseUp({});return!1}this.position=d.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";a.ui.ddmanager&&a.ui.ddmanager.drag(this,b);return!1},_mouseStop:function(b){var c=!1;a.ui.ddmanager&&!this.options.dropBehaviour&&(c=a.ui.ddmanager.drop(this,b)),this.dropped&&(c=this.dropped,this.dropped=!1);if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return!1;if(this.options.revert=="invalid"&&!c||this.options.revert=="valid"&&c||this.options.revert===!0||a.isFunction(this.options.revert)&&this.options.revert.call(this.element,c)){var d=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){d._trigger("stop",b)!==!1&&d._clear()})}else this._trigger("stop",b)!==!1&&this._clear();return!1},_mouseUp:function(b){this.options.iframeFix===!0&&a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),a.ui.ddmanager&&a.ui.ddmanager.dragStop(this,b);return a.ui.mouse.prototype._mouseUp.call(this,b)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?!0:!1;a(this.options.handle,this.element).find("*").andSelf().each(function(){this==b.target&&(c=!0)});return c},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b])):c.helper=="clone"?this.element.clone().removeAttr("id"):this.element;d.parents("body").length||d.appendTo(c.appendTo=="parent"?this.element[0].parentNode:c.appendTo),d[0]!=this.element[0]&&!/(fixed|absolute)/.test(d.css("position"))&&d.css("position","absolute");return d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[b.containment=="document"?0:a(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,b.containment=="document"?0:a(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(b.containment=="document"?0:a(window).scrollLeft())+a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(b.containment=="document"?0:a(window).scrollTop())+(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)&&b.containment.constructor!=Array){var c=a(b.containment),d=c[0];if(!d)return;var e=c.offset(),f=a(d).css("overflow")!="hidden";this.containment=[(parseInt(a(d).css("borderLeftWidth"),10)||0)+(parseInt(a(d).css("paddingLeft"),10)||0),(parseInt(a(d).css("borderTopWidth"),10)||0)+(parseInt(a(d).css("paddingTop"),10)||0),(f?Math.max(d.scrollWidth,d.offsetWidth):d.offsetWidth)-(parseInt(a(d).css("borderLeftWidth"),10)||0)-(parseInt(a(d).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(f?Math.max(d.scrollHeight,d.offsetHeight):d.offsetHeight)-(parseInt(a(d).css("borderTopWidth"),10)||0)-(parseInt(a(d).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=c}else b.containment.constructor==Array&&(this.containment=b.containment)},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName),f=b.pageX,g=b.pageY;if(this.originalPosition){var h;if(this.containment){if(this.relative_container){var i=this.relative_container.offset();h=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]}else h=this.containment;b.pageX-this.offset.click.left<h[0]&&(f=h[0]+this.offset.click.left),b.pageY-this.offset.click.top<h[1]&&(g=h[1]+this.offset.click.top),b.pageX-this.offset.click.left>h[2]&&(f=h[2]+this.offset.click.left),b.pageY-this.offset.click.top>h[3]&&(g=h[3]+this.offset.click.top)}if(c.grid){var j=c.grid[1]?this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1]:this.originalPageY;g=h?j-this.offset.click.top<h[1]||j-this.offset.click.top>h[3]?j-this.offset.click.top<h[1]?j+c.grid[1]:j-c.grid[1]:j:j;var k=c.grid[0]?this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0]:this.originalPageX;f=h?k-this.offset.click.left<h[0]||k-this.offset.click.left>h[2]?k-this.offset.click.left<h[0]?k+c.grid[0]:k-c.grid[0]:k:k}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(b,c,d){d=d||this._uiHash(),a.ui.plugin.call(this,b,[c,d]),b=="drag"&&(this.positionAbs=this._convertPositionTo("absolute"));return a.Widget.prototype._trigger.call(this,b,c,d)},plugins:{},_uiHash:function(a){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),a.extend(a.ui.draggable,{version:"1.8.17"}),a.ui.plugin.add("draggable","connectToSortable",{start:function(b,c){var d=a(this).data("draggable"),e=d.options,f=a.extend({},c,{item:d.element});d.sortables=[],a(e.connectToSortable).each(function(){var c=a.data(this,"sortable");c&&!c.options.disabled&&(d.sortables.push({instance:c,shouldRevert:c.options.revert}),c.refreshPositions(),c._trigger("activate",b,f))})},stop:function(b,c){var d=a(this).data("draggable"),e=a.extend({},c,{item:d.element});a.each(d.sortables,function(){this.instance.isOver?(this.instance.isOver=0,d.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=!0),this.instance._mouseStop(b),this.instance.options.helper=this.instance.options._helper,d.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",b,e))})},drag:function(b,c){var d=a(this).data("draggable"),e=this,f=function(b){var c=this.offset.click.top,d=this.offset.click.left,e=this.positionAbs.top,f=this.positionAbs.left,g=b.height,h=b.width,i=b.top,j=b.left;return a.ui.isOver(e+c,f+d,i,j,g,h)};a.each(d.sortables,function(f){this.instance.positionAbs=d.positionAbs,this.instance.helperProportions=d.helperProportions,this.instance.offset.click=d.offset.click,this.instance._intersectsWith(this.instance.containerCache)?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=a(e).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return c.helper[0]},b.target=this.instance.currentItem[0],this.instance._mouseCapture(b,!0),this.instance._mouseStart(b,!0,!0),this.instance.offset.click.top=d.offset.click.top,this.instance.offset.click.left=d.offset.click.left,this.instance.offset.parent.left-=d.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=d.offset.parent.top-this.instance.offset.parent.top,d._trigger("toSortable",b),d.dropped=this.instance.element,d.currentItem=d.element,this.instance.fromOutside=d),this.instance.currentItem&&this.instance._mouseDrag(b)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",b,this.instance._uiHash(this.instance)),this.instance._mouseStop(b,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),d._trigger("fromSortable",b),d.dropped=!1)})}}),a.ui.plugin.add("draggable","cursor",{start:function(b,c){var d=a("body"),e=a(this).data("draggable").options;d.css("cursor")&&(e._cursor=d.css("cursor")),d.css("cursor",e.cursor)},stop:function(b,c){var d=a(this).data("draggable").options;d._cursor&&a("body").css("cursor",d._cursor)}}),a.ui.plugin.add("draggable","opacity",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("opacity")&&(e._opacity=d.css("opacity")),d.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;d._opacity&&a(c.helper).css("opacity",d._opacity)}}),a.ui.plugin.add("draggable","scroll",{start:function(b,c){var d=a(this).data("draggable");d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"&&(d.overflowOffset=d.scrollParent.offset())},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=!1;if(d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"){if(!e.axis||e.axis!="x")d.overflowOffset.top+d.scrollParent[0].offsetHeight-b.pageY<e.scrollSensitivity?d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop+e.scrollSpeed:b.pageY-d.overflowOffset.top<e.scrollSensitivity&&(d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop-e.scrollSpeed);if(!e.axis||e.axis!="y")d.overflowOffset.left+d.scrollParent[0].offsetWidth-b.pageX<e.scrollSensitivity?d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft+e.scrollSpeed:b.pageX-d.overflowOffset.left<e.scrollSensitivity&&(d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft-e.scrollSpeed)}else{if(!e.axis||e.axis!="x")b.pageY-a(document).scrollTop()<e.scrollSensitivity?f=a(document).scrollTop(a(document).scrollTop()-e.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<e.scrollSensitivity&&(f=a(document).scrollTop(a(document).scrollTop()+e.scrollSpeed));if(!e.axis||e.axis!="y")b.pageX-a(document).scrollLeft()<e.scrollSensitivity?f=a(document).scrollLeft(a(document).scrollLeft()-e.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<e.scrollSensitivity&&(f=a(document).scrollLeft(a(document).scrollLeft()+e.scrollSpeed))}f!==!1&&a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(d,b)}}),a.ui.plugin.add("draggable","snap",{start:function(b,c){var d=a(this).data("draggable"),e=d.options;d.snapElements=[],a(e.snap.constructor!=String?e.snap.items||":data(draggable)":e.snap).each(function(){var b=a(this),c=b.offset();this!=d.element[0]&&d.snapElements.push({item:this,width:b.outerWidth(),height:b.outerHeight(),top:c.top,left:c.left})})},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=e.snapTolerance,g=c.offset.left,h=g+d.helperProportions.width,i=c.offset.top,j=i+d.helperProportions.height;for(var k=d.snapElements.length-1;k>=0;k--){var l=d.snapElements[k].left,m=l+d.snapElements[k].width,n=d.snapElements[k].top,o=n+d.snapElements[k].height;if(!(l-f<g&&g<m+f&&n-f<i&&i<o+f||l-f<g&&g<m+f&&n-f<j&&j<o+f||l-f<h&&h<m+f&&n-f<i&&i<o+f||l-f<h&&h<m+f&&n-f<j&&j<o+f)){d.snapElements[k].snapping&&d.options.snap.release&&d.options.snap.release.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=!1;continue}if(e.snapMode!="inner"){var p=Math.abs(n-j)<=f,q=Math.abs(o-i)<=f,r=Math.abs(l-h)<=f,s=Math.abs(m-g)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n-d.helperProportions.height,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l-d.helperProportions.width}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m}).left-d.margins.left)}var t=p||q||r||s;if(e.snapMode!="outer"){var p=Math.abs(n-i)<=f,q=Math.abs(o-j)<=f,r=Math.abs(l-g)<=f,s=Math.abs(m-h)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o-d.helperProportions.height,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m-d.helperProportions.width}).left-d.margins.left)}!d.snapElements[k].snapping&&(p||q||r||s||t)&&d.options.snap.snap&&d.options.snap.snap.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=p||q||r||s||t}}}),a.ui.plugin.add("draggable","stack",{start:function(b,c){var d=a(this).data("draggable").options,e=a.makeArray(a(d.stack)).sort(function(b,c){return(parseInt(a(b).css("zIndex"),10)||0)-(parseInt(a(c).css("zIndex"),10)||0)});if(!!e.length){var f=parseInt(e[0].style.zIndex)||0;a(e).each(function(a){this.style.zIndex=f+a}),this[0].style.zIndex=f+e.length}}}),a.ui.plugin.add("draggable","zIndex",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("zIndex")&&(e._zIndex=d.css("zIndex")),d.css("zIndex",e.zIndex)},stop:function(b,c){var d=a(this).data("draggable").options;d._zIndex&&a(c.helper).css("zIndex",d._zIndex)}})})(jQuery);/*
50
+ * jQuery UI Droppable 1.8.17
51
+ *
52
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
53
+ * Dual licensed under the MIT or GPL Version 2 licenses.
54
+ * http://jquery.org/license
55
+ *
56
+ * http://docs.jquery.com/UI/Droppables
57
+ *
58
+ * Depends:
59
+ * jquery.ui.core.js
60
+ * jquery.ui.widget.js
61
+ * jquery.ui.mouse.js
62
+ * jquery.ui.draggable.js
63
+ */(function(a,b){a.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect"},_create:function(){var b=this.options,c=b.accept;this.isover=0,this.isout=1,this.accept=a.isFunction(c)?c:function(a){return a.is(c)},this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight},a.ui.ddmanager.droppables[b.scope]=a.ui.ddmanager.droppables[b.scope]||[],a.ui.ddmanager.droppables[b.scope].push(this),b.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){var b=a.ui.ddmanager.droppables[this.options.scope];for(var c=0;c<b.length;c++)b[c]==this&&b.splice(c,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(b,c){b=="accept"&&(this.accept=a.isFunction(c)?c:function(a){return a.is(c)}),a.Widget.prototype._setOption.apply(this,arguments)},_activate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),c&&this._trigger("activate",b,this.ui(c))},_deactivate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),c&&this._trigger("deactivate",b,this.ui(c))},_over:function(b){var c=a.ui.ddmanager.current;!!c&&(c.currentItem||c.element)[0]!=this.element[0]&&this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",b,this.ui(c)))},_out:function(b){var c=a.ui.ddmanager.current;!!c&&(c.currentItem||c.element)[0]!=this.element[0]&&this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",b,this.ui(c)))},_drop:function(b,c){var d=c||a.ui.ddmanager.current;if(!d||(d.currentItem||d.element)[0]==this.element[0])return!1;var e=!1;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var b=a.data(this,"droppable");if(b.options.greedy&&!b.options.disabled&&b.options.scope==d.options.scope&&b.accept.call(b.element[0],d.currentItem||d.element)&&a.ui.intersect(d,a.extend(b,{offset:b.element.offset()}),b.options.tolerance)){e=!0;return!1}});if(e)return!1;if(this.accept.call(this.element[0],d.currentItem||d.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",b,this.ui(d));return this.element}return!1},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}}),a.extend(a.ui.droppable,{version:"1.8.17"}),a.ui.intersect=function(b,c,d){if(!c.offset)return!1;var e=(b.positionAbs||b.position.absolute).left,f=e+b.helperProportions.width,g=(b.positionAbs||b.position.absolute).top,h=g+b.helperProportions.height,i=c.offset.left,j=i+c.proportions.width,k=c.offset.top,l=k+c.proportions.height;switch(d){case"fit":return i<=e&&f<=j&&k<=g&&h<=l;case"intersect":return i<e+b.helperProportions.width/2&&f-b.helperProportions.width/2<j&&k<g+b.helperProportions.height/2&&h-b.helperProportions.height/2<l;case"pointer":var m=(b.positionAbs||b.position.absolute).left+(b.clickOffset||b.offset.click).left,n=(b.positionAbs||b.position.absolute).top+(b.clickOffset||b.offset.click).top,o=a.ui.isOver(n,m,k,i,c.proportions.height,c.proportions.width);return o;case"touch":return(g>=k&&g<=l||h>=k&&h<=l||g<k&&h>l)&&(e>=i&&e<=j||f>=i&&f<=j||e<i&&f>j);default:return!1}},a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(b,c){var d=a.ui.ddmanager.droppables[b.options.scope]||[],e=c?c.type:null,f=(b.currentItem||b.element).find(":data(droppable)").andSelf();droppablesLoop:for(var g=0;g<d.length;g++){if(d[g].options.disabled||b&&!d[g].accept.call(d[g].element[0],b.currentItem||b.element))continue;for(var h=0;h<f.length;h++)if(f[h]==d[g].element[0]){d[g].proportions.height=0;continue droppablesLoop}d[g].visible=d[g].element.css("display")!="none";if(!d[g].visible)continue;e=="mousedown"&&d[g]._activate.call(d[g],c),d[g].offset=d[g].element.offset(),d[g].proportions={width:d[g].element[0].offsetWidth,height:d[g].element[0].offsetHeight}}},drop:function(b,c){var d=!1;a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){!this.options||(!this.options.disabled&&this.visible&&a.ui.intersect(b,this,this.options.tolerance)&&(d=this._drop.call(this,c)||d),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],b.currentItem||b.element)&&(this.isout=1,this.isover=0,this._deactivate.call(this,c)))});return d},dragStart:function(b,c){b.element.parents(":not(body,html)").bind("scroll.droppable",function(){b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)})},drag:function(b,c){b.options.refreshPositions&&a.ui.ddmanager.prepareOffsets(b,c),a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var d=a.ui.intersect(b,this,this.options.tolerance),e=!d&&this.isover==1?"isout":d&&this.isover==0?"isover":null;if(!e)return;var f;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");g.length&&(f=a.data(g[0],"droppable"),f.greedyChild=e=="isover"?1:0)}f&&e=="isover"&&(f.isover=0,f.isout=1,f._out.call(f,c)),this[e]=1,this[e=="isout"?"isover":"isout"]=0,this[e=="isover"?"_over":"_out"].call(this,c),f&&e=="isout"&&(f.isout=0,f.isover=1,f._over.call(f,c))}})},dragStop:function(b,c){b.element.parents(":not(body,html)").unbind("scroll.droppable"),b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)}}})(jQuery);/*
64
+ * jQuery UI Resizable 1.8.17
65
+ *
66
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
67
+ * Dual licensed under the MIT or GPL Version 2 licenses.
68
+ * http://jquery.org/license
69
+ *
70
+ * http://docs.jquery.com/UI/Resizables
71
+ *
72
+ * Depends:
73
+ * jquery.ui.core.js
74
+ * jquery.ui.mouse.js
75
+ * jquery.ui.widget.js
76
+ */(function(a,b){a.widget("ui.resizable",a.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1e3},_create:function(){var b=this,c=this.options;this.element.addClass("ui-resizable"),a.extend(this,{_aspectRatio:!!c.aspectRatio,aspectRatio:c.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:c.helper||c.ghost||c.animate?c.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(/relative/.test(this.element.css("position"))&&a.browser.opera&&this.element.css({position:"relative",top:"auto",left:"auto"}),this.element.wrap(a('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("resizable",this.element.data("resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=c.handles||(a(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se");if(this.handles.constructor==String){this.handles=="all"&&(this.handles="n,e,s,w,se,sw,ne,nw");var d=this.handles.split(",");this.handles={};for(var e=0;e<d.length;e++){var f=a.trim(d[e]),g="ui-resizable-"+f,h=a('<div class="ui-resizable-handle '+g+'"></div>');/sw|se|ne|nw/.test(f)&&h.css({zIndex:++c.zIndex}),"se"==f&&h.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[f]=".ui-resizable-"+f,this.element.append(h)}}this._renderAxis=function(b){b=b||this.element;for(var c in this.handles){this.handles[c].constructor==String&&(this.handles[c]=a(this.handles[c],this.element).show());if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var d=a(this.handles[c],this.element),e=0;e=/sw|ne|nw|se|n|s/.test(c)?d.outerHeight():d.outerWidth();var f=["padding",/ne|nw|n/.test(c)?"Top":/se|sw|s/.test(c)?"Bottom":/^e$/.test(c)?"Right":"Left"].join("");b.css(f,e),this._proportionallyResize()}if(!a(this.handles[c]).length)continue}},this._renderAxis(this.element),this._handles=a(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){if(!b.resizing){if(this.className)var a=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=a&&a[1]?a[1]:"se"}}),c.autoHide&&(this._handles.hide(),a(this.element).addClass("ui-resizable-autohide").hover(function(){c.disabled||(a(this).removeClass("ui-resizable-autohide"),b._handles.show())},function(){c.disabled||b.resizing||(a(this).addClass("ui-resizable-autohide"),b._handles.hide())})),this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(b){a(b).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var c=this.element;c.after(this.originalElement.css({position:c.css("position"),width:c.outerWidth(),height:c.outerHeight(),top:c.css("top"),left:c.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle),b(this.originalElement);return this},_mouseCapture:function(b){var c=!1;for(var d in this.handles)a(this.handles[d])[0]==b.target&&(c=!0);return!this.options.disabled&&c},_mouseStart:function(b){var d=this.options,e=this.element.position(),f=this.element;this.resizing=!0,this.documentScroll={top:a(document).scrollTop(),left:a(document).scrollLeft()},(f.is(".ui-draggable")||/absolute/.test(f.css("position")))&&f.css({position:"absolute",top:e.top,left:e.left}),a.browser.opera&&/relative/.test(f.css("position"))&&f.css({position:"relative",top:"auto",left:"auto"}),this._renderProxy();var g=c(this.helper.css("left")),h=c(this.helper.css("top"));d.containment&&(g+=a(d.containment).scrollLeft()||0,h+=a(d.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:g,top:h},this.size=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalSize=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalPosition={left:g,top:h},this.sizeDiff={width:f.outerWidth()-f.width(),height:f.outerHeight()-f.height()},this.originalMousePosition={left:b.pageX,top:b.pageY},this.aspectRatio=typeof d.aspectRatio=="number"?d.aspectRatio:this.originalSize.width/this.originalSize.height||1;var i=a(".ui-resizable-"+this.axis).css("cursor");a("body").css("cursor",i=="auto"?this.axis+"-resize":i),f.addClass("ui-resizable-resizing"),this._propagate("start",b);return!0},_mouseDrag:function(b){var c=this.helper,d=this.options,e={},f=this,g=this.originalMousePosition,h=this.axis,i=b.pageX-g.left||0,j=b.pageY-g.top||0,k=this._change[h];if(!k)return!1;var l=k.apply(this,[b,i,j]),m=a.browser.msie&&a.browser.version<7,n=this.sizeDiff;this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)l=this._updateRatio(l,b);l=this._respectSize(l,b),this._propagate("resize",b),c.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"}),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),this._updateCache(l),this._trigger("resize",b,this.ui());return!1},_mouseStop:function(b){this.resizing=!1;var c=this.options,d=this;if(this._helper){var e=this._proportionallyResizeElements,f=e.length&&/textarea/i.test(e[0].nodeName),g=f&&a.ui.hasScroll(e[0],"left")?0:d.sizeDiff.height,h=f?0:d.sizeDiff.width,i={width:d.helper.width()-h,height:d.helper.height()-g},j=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,k=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;c.animate||this.element.css(a.extend(i,{top:k,left:j})),d.helper.height(d.size.height),d.helper.width(d.size.width),this._helper&&!c.animate&&this._proportionallyResize()}a("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",b),this._helper&&this.helper.remove();return!1},_updateVirtualBoundaries:function(a){var b=this.options,c,e,f,g,h;h={minWidth:d(b.minWidth)?b.minWidth:0,maxWidth:d(b.maxWidth)?b.maxWidth:Infinity,minHeight:d(b.minHeight)?b.minHeight:0,maxHeight:d(b.maxHeight)?b.maxHeight:Infinity};if(this._aspectRatio||a)c=h.minHeight*this.aspectRatio,f=h.minWidth/this.aspectRatio,e=h.maxHeight*this.aspectRatio,g=h.maxWidth/this.aspectRatio,c>h.minWidth&&(h.minWidth=c),f>h.minHeight&&(h.minHeight=f),e<h.maxWidth&&(h.maxWidth=e),g<h.maxHeight&&(h.maxHeight=g);this._vBoundaries=h},_updateCache:function(a){var b=this.options;this.offset=this.helper.offset(),d(a.left)&&(this.position.left=a.left),d(a.top)&&(this.position.top=a.top),d(a.height)&&(this.size.height=a.height),d(a.width)&&(this.size.width=a.width)},_updateRatio:function(a,b){var c=this.options,e=this.position,f=this.size,g=this.axis;d(a.height)?a.width=a.height*this.aspectRatio:d(a.width)&&(a.height=a.width/this.aspectRatio),g=="sw"&&(a.left=e.left+(f.width-a.width),a.top=null),g=="nw"&&(a.top=e.top+(f.height-a.height),a.left=e.left+(f.width-a.width));return a},_respectSize:function(a,b){var c=this.helper,e=this._vBoundaries,f=this._aspectRatio||b.shiftKey,g=this.axis,h=d(a.width)&&e.maxWidth&&e.maxWidth<a.width,i=d(a.height)&&e.maxHeight&&e.maxHeight<a.height,j=d(a.width)&&e.minWidth&&e.minWidth>a.width,k=d(a.height)&&e.minHeight&&e.minHeight>a.height;j&&(a.width=e.minWidth),k&&(a.height=e.minHeight),h&&(a.width=e.maxWidth),i&&(a.height=e.maxHeight);var l=this.originalPosition.left+this.originalSize.width,m=this.position.top+this.size.height,n=/sw|nw|w/.test(g),o=/nw|ne|n/.test(g);j&&n&&(a.left=l-e.minWidth),h&&n&&(a.left=l-e.maxWidth),k&&o&&(a.top=m-e.minHeight),i&&o&&(a.top=m-e.maxHeight);var p=!a.width&&!a.height;p&&!a.left&&a.top?a.top=null:p&&!a.top&&a.left&&(a.left=null);return a},_proportionallyResize:function(){var b=this.options;if(!!this._proportionallyResizeElements.length){var c=this.helper||this.element;for(var d=0;d<this._proportionallyResizeElements.length;d++){var e=this._proportionallyResizeElements[d];if(!this.borderDif){var f=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],g=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];this.borderDif=a.map(f,function(a,b){var c=parseInt(a,10)||0,d=parseInt(g[b],10)||0;return c+d})}if(a.browser.msie&&(!!a(c).is(":hidden")||!!a(c).parents(":hidden").length))continue;e.css({height:c.height()-this.borderDif[0]-this.borderDif[2]||0,width:c.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var b=this.element,c=this.options;this.elementOffset=b.offset();if(this._helper){this.helper=this.helper||a('<div style="overflow:hidden;"></div>');var d=a.browser.msie&&a.browser.version<7,e=d?1:0,f=d?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+f,height:this.element.outerHeight()+f,position:"absolute",left:this.elementOffset.left-e+"px",top:this.elementOffset.top-e+"px",zIndex:++c.zIndex}),this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(a,b,c){return{width:this.originalSize.width+b}},w:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{left:f.left+b,width:e.width-b}},n:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{top:f.top+c,height:e.height-c}},s:function(a,b,c){return{height:this.originalSize.height+c}},se:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},sw:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,c,d]))},ne:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},nw:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,c,d]))}},_propagate:function(b,c){a.ui.plugin.call(this,b,[c,this.ui()]),b!="resize"&&this._trigger(b,c,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),a.extend(a.ui.resizable,{version:"1.8.17"}),a.ui.plugin.add("resizable","alsoResize",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=function(b){a(b).each(function(){var b=a(this);b.data("resizable-alsoresize",{width:parseInt(b.width(),10),height:parseInt(b.height(),10),left:parseInt(b.css("left"),10),top:parseInt(b.css("top"),10),position:b.css("position")})})};typeof e.alsoResize=="object"&&!e.alsoResize.parentNode?e.alsoResize.length?(e.alsoResize=e.alsoResize[0],f(e.alsoResize)):a.each(e.alsoResize,function(a){f(a)}):f(e.alsoResize)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.originalSize,g=d.originalPosition,h={height:d.size.height-f.height||0,width:d.size.width-f.width||0,top:d.position.top-g.top||0,left:d.position.left-g.left||0},i=function(b,e){a(b).each(function(){var b=a(this),f=a(this).data("resizable-alsoresize"),g={},i=e&&e.length?e:b.parents(c.originalElement[0]).length?["width","height"]:["width","height","top","left"];a.each(i,function(a,b){var c=(f[b]||0)+(h[b]||0);c&&c>=0&&(g[b]=c||null)}),a.browser.opera&&/relative/.test(b.css("position"))&&(d._revertToRelativePosition=!0,b.css({position:"absolute",top:"auto",left:"auto"})),b.css(g)})};typeof e.alsoResize=="object"&&!e.alsoResize.nodeType?a.each(e.alsoResize,function(a,b){i(a,b)}):i(e.alsoResize)},stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=function(b){a(b).each(function(){var b=a(this);b.css({position:b.data("resizable-alsoresize").position})})};d._revertToRelativePosition&&(d._revertToRelativePosition=!1,typeof e.alsoResize=="object"&&!e.alsoResize.nodeType?a.each(e.alsoResize,function(a){f(a)}):f(e.alsoResize)),a(this).removeData("resizable-alsoresize")}}),a.ui.plugin.add("resizable","animate",{stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d._proportionallyResizeElements,g=f.length&&/textarea/i.test(f[0].nodeName),h=g&&a.ui.hasScroll(f[0],"left")?0:d.sizeDiff.height,i=g?0:d.sizeDiff.width,j={width:d.size.width-i,height:d.size.height-h},k=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,l=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;d.element.animate(a.extend(j,l&&k?{top:l,left:k}:{}),{duration:e.animateDuration,easing:e.animateEasing,step:function(){var c={width:parseInt(d.element.css("width"),10),height:parseInt(d.element.css("height"),10),top:parseInt(d.element.css("top"),10),left:parseInt(d.element.css("left"),10)};f&&f.length&&a(f[0]).css({width:c.width,height:c.height}),d._updateCache(c),d._propagate("resize",b)}})}}),a.ui.plugin.add("resizable","containment",{start:function(b,d){var e=a(this).data("resizable"),f=e.options,g=e.element,h=f.containment,i=h instanceof a?h.get(0):/parent/.test(h)?g.parent().get(0):h;if(!!i){e.containerElement=a(i);if(/document/.test(h)||h==document)e.containerOffset={left:0,top:0},e.containerPosition={left:0,top:0},e.parentData={element:a(document),left:0,top:0,width:a(document).width(),height:a(document).height()||document.body.parentNode.scrollHeight};else{var j=a(i),k=[];a(["Top","Right","Left","Bottom"]).each(function(a,b){k[a]=c(j.css("padding"+b))}),e.containerOffset=j.offset(),e.containerPosition=j.position(),e.containerSize={height:j.innerHeight()-k[3],width:j.innerWidth()-k[1]};var l=e.containerOffset,m=e.containerSize.height,n=e.containerSize.width,o=a.ui.hasScroll(i,"left")?i.scrollWidth:n,p=a.ui.hasScroll(i)?i.scrollHeight:m;e.parentData={element:i,left:l.left,top:l.top,width:o,height:p}}}},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.containerSize,g=d.containerOffset,h=d.size,i=d.position,j=d._aspectRatio||b.shiftKey,k={top:0,left:0},l=d.containerElement;l[0]!=document&&/static/.test(l.css("position"))&&(k=g),i.left<(d._helper?g.left:0)&&(d.size.width=d.size.width+(d._helper?d.position.left-g.left:d.position.left-k.left),j&&(d.size.height=d.size.width/e.aspectRatio),d.position.left=e.helper?g.left:0),i.top<(d._helper?g.top:0)&&(d.size.height=d.size.height+(d._helper?d.position.top-g.top:d.position.top),j&&(d.size.width=d.size.height*e.aspectRatio),d.position.top=d._helper?g.top:0),d.offset.left=d.parentData.left+d.position.left,d.offset.top=d.parentData.top+d.position.top;var m=Math.abs((d._helper?d.offset.left-k.left:d.offset.left-k.left)+d.sizeDiff.width),n=Math.abs((d._helper?d.offset.top-k.top:d.offset.top-g.top)+d.sizeDiff.height),o=d.containerElement.get(0)==d.element.parent().get(0),p=/relative|absolute/.test(d.containerElement.css("position"));o&&p&&(m-=d.parentData.left),m+d.size.width>=d.parentData.width&&(d.size.width=d.parentData.width-m,j&&(d.size.height=d.size.width/d.aspectRatio)),n+d.size.height>=d.parentData.height&&(d.size.height=d.parentData.height-n,j&&(d.size.width=d.size.height*d.aspectRatio))},stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.position,g=d.containerOffset,h=d.containerPosition,i=d.containerElement,j=a(d.helper),k=j.offset(),l=j.outerWidth()-d.sizeDiff.width,m=j.outerHeight()-d.sizeDiff.height;d._helper&&!e.animate&&/relative/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m}),d._helper&&!e.animate&&/static/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m})}}),a.ui.plugin.add("resizable","ghost",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size;d.ghost=d.originalElement.clone(),d.ghost.css({opacity:.25,display:"block",position:"relative",height:f.height,width:f.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof e.ghost=="string"?e.ghost:""),d.ghost.appendTo(d.helper)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.ghost.css({position:"relative",height:d.size.height,width:d.size.width})},stop:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.helper&&d.helper.get(0).removeChild(d.ghost.get(0))}}),a.ui.plugin.add("resizable","grid",{resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size,g=d.originalSize,h=d.originalPosition,i=d.axis,j=e._aspectRatio||b.shiftKey;e.grid=typeof e.grid=="number"?[e.grid,e.grid]:e.grid;var k=Math.round((f.width-g.width)/(e.grid[0]||1))*(e.grid[0]||1),l=Math.round((f.height-g.height)/(e.grid[1]||1))*(e.grid[1]||1);/^(se|s|e)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l):/^(ne)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l):/^(sw)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.left=h.left-k):(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l,d.position.left=h.left-k)}});var c=function(a){return parseInt(a,10)||0},d=function(a){return!isNaN(parseInt(a,10))}})(jQuery);/*
77
+ * jQuery UI Selectable 1.8.17
78
+ *
79
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
80
+ * Dual licensed under the MIT or GPL Version 2 licenses.
81
+ * http://jquery.org/license
82
+ *
83
+ * http://docs.jquery.com/UI/Selectables
84
+ *
85
+ * Depends:
86
+ * jquery.ui.core.js
87
+ * jquery.ui.mouse.js
88
+ * jquery.ui.widget.js
89
+ */(function(a,b){a.widget("ui.selectable",a.ui.mouse,{options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch"},_create:function(){var b=this;this.element.addClass("ui-selectable"),this.dragged=!1;var c;this.refresh=function(){c=a(b.options.filter,b.element[0]),c.addClass("ui-selectee"),c.each(function(){var b=a(this),c=b.offset();a.data(this,"selectable-item",{element:this,$element:b,left:c.left,top:c.top,right:c.left+b.outerWidth(),bottom:c.top+b.outerHeight(),startselected:!1,selected:b.hasClass("ui-selected"),selecting:b.hasClass("ui-selecting"),unselecting:b.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=c.addClass("ui-selectee"),this._mouseInit(),this.helper=a("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable"),this._mouseDestroy();return this},_mouseStart:function(b){var c=this;this.opos=[b.pageX,b.pageY];if(!this.options.disabled){var d=this.options;this.selectees=a(d.filter,this.element[0]),this._trigger("start",b),a(d.appendTo).append(this.helper),this.helper.css({left:b.clientX,top:b.clientY,width:0,height:0}),d.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var d=a.data(this,"selectable-item");d.startselected=!0,!b.metaKey&&!b.ctrlKey&&(d.$element.removeClass("ui-selected"),d.selected=!1,d.$element.addClass("ui-unselecting"),d.unselecting=!0,c._trigger("unselecting",b,{unselecting:d.element}))}),a(b.target).parents().andSelf().each(function(){var d=a.data(this,"selectable-item");if(d){var e=!b.metaKey&&!b.ctrlKey||!d.$element.hasClass("ui-selected");d.$element.removeClass(e?"ui-unselecting":"ui-selected").addClass(e?"ui-selecting":"ui-unselecting"),d.unselecting=!e,d.selecting=e,d.selected=e,e?c._trigger("selecting",b,{selecting:d.element}):c._trigger("unselecting",b,{unselecting:d.element});return!1}})}},_mouseDrag:function(b){var c=this;this.dragged=!0;if(!this.options.disabled){var d=this.options,e=this.opos[0],f=this.opos[1],g=b.pageX,h=b.pageY;if(e>g){var i=g;g=e,e=i}if(f>h){var i=h;h=f,f=i}this.helper.css({left:e,top:f,width:g-e,height:h-f}),this.selectees.each(function(){var i=a.data(this,"selectable-item");if(!!i&&i.element!=c.element[0]){var j=!1;d.tolerance=="touch"?j=!(i.left>g||i.right<e||i.top>h||i.bottom<f):d.tolerance=="fit"&&(j=i.left>e&&i.right<g&&i.top>f&&i.bottom<h),j?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,c._trigger("selecting",b,{selecting:i.element}))):(i.selecting&&((b.metaKey||b.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),c._trigger("unselecting",b,{unselecting:i.element}))),i.selected&&!b.metaKey&&!b.ctrlKey&&!i.startselected&&(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,c._trigger("unselecting",b,{unselecting:i.element})))}});return!1}},_mouseStop:function(b){var c=this;this.dragged=!1;var d=this.options;a(".ui-unselecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-unselecting"),d.unselecting=!1,d.startselected=!1,c._trigger("unselected",b,{unselected:d.element})}),a(".ui-selecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected"),d.selecting=!1,d.selected=!0,d.startselected=!0,c._trigger("selected",b,{selected:d.element})}),this._trigger("stop",b),this.helper.remove();return!1}}),a.extend(a.ui.selectable,{version:"1.8.17"})})(jQuery);/*
90
+ * jQuery UI Sortable 1.8.17
91
+ *
92
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
93
+ * Dual licensed under the MIT or GPL Version 2 licenses.
94
+ * http://jquery.org/license
95
+ *
96
+ * http://docs.jquery.com/UI/Sortables
97
+ *
98
+ * Depends:
99
+ * jquery.ui.core.js
100
+ * jquery.ui.mouse.js
101
+ * jquery.ui.widget.js
102
+ */(function(a,b){a.widget("ui.sortable",a.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3},_create:function(){var a=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):!1,this.offset=this.element.offset(),this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData(this.widgetName+"-item");return this},_setOption:function(b,c){b==="disabled"?(this.options[b]=c,this.widget()[c?"addClass":"removeClass"]("ui-sortable-disabled")):a.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(b,c){var d=this;if(this.reverting)return!1;if(this.options.disabled||this.options.type=="static")return!1;this._refreshItems(b);var e=null,f=this,g=a(b.target).parents().each(function(){if(a.data(this,d.widgetName+"-item")==f){e=a(this);return!1}});a.data(b.target,d.widgetName+"-item")==f&&(e=a(b.target));if(!e)return!1;if(this.options.handle&&!c){var h=!1;a(this.options.handle,e).find("*").andSelf().each(function(){this==b.target&&(h=!0)});if(!h)return!1}this.currentItem=e,this._removeCurrentsFromItems();return!0},_mouseStart:function(b,c,d){var e=this.options,f=this;this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(b),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!=this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),e.containment&&this._setContainment(),e.cursor&&(a("body").css("cursor")&&(this._storedCursor=a("body").css("cursor")),a("body").css("cursor",e.cursor)),e.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",e.opacity)),e.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",e.zIndex)),this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",b,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions();if(!d)for(var g=this.containers.length-1;g>=0;g--)this.containers[g]._trigger("activate",b,f._uiHash(this));a.ui.ddmanager&&(a.ui.ddmanager.current=this),a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(b);return!0},_mouseDrag:function(b){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);if(this.options.scroll){var c=this.options,d=!1;this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-b.pageY<c.scrollSensitivity?this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop+c.scrollSpeed:b.pageY-this.overflowOffset.top<c.scrollSensitivity&&(this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop-c.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-b.pageX<c.scrollSensitivity?this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft+c.scrollSpeed:b.pageX-this.overflowOffset.left<c.scrollSensitivity&&(this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft-c.scrollSpeed)):(b.pageY-a(document).scrollTop()<c.scrollSensitivity?d=a(document).scrollTop(a(document).scrollTop()-c.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<c.scrollSensitivity&&(d=a(document).scrollTop(a(document).scrollTop()+c.scrollSpeed)),b.pageX-a(document).scrollLeft()<c.scrollSensitivity?d=a(document).scrollLeft(a(document).scrollLeft()-c.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<c.scrollSensitivity&&(d=a(document).scrollLeft(a(document).scrollLeft()+c.scrollSpeed))),d!==!1&&a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(var e=this.items.length-1;e>=0;e--){var f=this.items[e],g=f.item[0],h=this._intersectsWithPointer(f);if(!h)continue;if(g!=this.currentItem[0]&&this.placeholder[h==1?"next":"prev"]()[0]!=g&&!a.ui.contains(this.placeholder[0],g)&&(this.options.type=="semi-dynamic"?!a.ui.contains(this.element[0],g):!0)){this.direction=h==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(f))this._rearrange(b,f);else break;this._trigger("change",b,this._uiHash());break}}this._contactContainers(b),a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),this._trigger("sort",b,this._uiHash()),this.lastPositionAbs=this.positionAbs;return!1},_mouseStop:function(b,c){if(!!b){a.ui.ddmanager&&!this.options.dropBehaviour&&a.ui.ddmanager.drop(this,b);if(this.options.revert){var d=this,e=d.placeholder.offset();d.reverting=!0,a(this.helper).animate({left:e.left-this.offset.parent.left-d.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:e.top-this.offset.parent.top-d.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){d._clear(b)})}else this._clear(b,c);return!1}},cancel:function(){var b=this;if(this.dragging){this._mouseUp({target:null}),this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("deactivate",null,b._uiHash(this)),this.containers[c].containerCache.over&&(this.containers[c]._trigger("out",null,b._uiHash(this)),this.containers[c].containerCache.over=0)}this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),a.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?a(this.domPosition.prev).after(this.currentItem):a(this.domPosition.parent).prepend(this.currentItem));return this},serialize:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];b=b||{},a(c).each(function(){var c=(a(b.item||this).attr(b.attribute||"id")||"").match(b.expression||/(.+)[-=_](.+)/);c&&d.push((b.key||c[1]+"[]")+"="+(b.key&&b.expression?c[1]:c[2]))}),!d.length&&b.key&&d.push(b.key+"=");return d.join("&")},toArray:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];b=b||{},c.each(function(){d.push(a(b.item||this).attr(b.attribute||"id")||"")});return d},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,d=this.positionAbs.top,e=d+this.helperProportions.height,f=a.left,g=f+a.width,h=a.top,i=h+a.height,j=this.offset.click.top,k=this.offset.click.left,l=d+j>h&&d+j<i&&b+k>f&&b+k<g;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?l:f<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<g&&h<d+this.helperProportions.height/2&&e-this.helperProportions.height/2<i},_intersectsWithPointer:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left,b.width),e=c&&d,f=this._getDragVerticalDirection(),g=this._getDragHorizontalDirection();if(!e)return!1;return this.floating?g&&g=="right"||f=="down"?2:1:f&&(f=="down"?2:1)},_intersectsWithSides:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top+b.height/2,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left+b.width/2,b.width),e=this._getDragVerticalDirection(),f=this._getDragHorizontalDirection();return this.floating&&f?f=="right"&&d||f=="left"&&!d:e&&(e=="down"&&c||e=="up"&&!c)},_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a),this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(b){var c=this,d=[],e=[],f=this._connectWith();if(f&&b)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&e.push([a.isFunction(j.options.items)?j.options.items.call(j.element):a(j.options.items,j.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),j])}}e.push([a.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):a(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(var g=e.length-1;g>=0;g--)e[g][0].each(function(){d.push(this)});return a(d)},_removeCurrentsFromItems:function(){var a=this.currentItem.find(":data("+this.widgetName+"-item)");for(var b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(b){this.items=[],this.containers=[this];var c=this.items,d=this,e=[[a.isFunction(this.options.items)?this.options.items.call(this.element[0],b,{item:this.currentItem}):a(this.options.items,this.element),this]],f=this._connectWith();if(f)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&(e.push([a.isFunction(j.options.items)?j.options.items.call(j.element[0],b,{item:this.currentItem}):a(j.options.items,j.element),j]),this.containers.push(j))}}for(var g=e.length-1;g>=0;g--){var k=e[g][1],l=e[g][0];for(var i=0,m=l.length;i<m;i++){var n=a(l[i]);n.data(this.widgetName+"-item",k),c.push({item:n,instance:k,width:0,height:0,left:0,top:0})}}},refreshPositions:function(b){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());for(var c=this.items.length-1;c>=0;c--){var d=this.items[c];if(d.instance!=this.currentContainer&&this.currentContainer&&d.item[0]!=this.currentItem[0])continue;var e=this.options.toleranceElement?a(this.options.toleranceElement,d.item):d.item;b||(d.width=e.outerWidth(),d.height=e.outerHeight());var f=e.offset();d.left=f.left,d.top=f.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(var c=this.containers.length-1;c>=0;c--){var f=this.containers[c].element.offset();this.containers[c].containerCache.left=f.left,this.containers[c].containerCache.top=f.top,this.containers[c].containerCache.width=this.containers[c].element.outerWidth(),this.containers[c].containerCache.height=this.containers[c].element.outerHeight()}return this},_createPlaceholder:function(b){var c=b||this,d=c.options;if(!d.placeholder||d.placeholder.constructor==String){var e=d.placeholder;d.placeholder={element:function(){var b=a(document.createElement(c.currentItem[0].nodeName)).addClass(e||c.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];e||(b.style.visibility="hidden");return b},update:function(a,b){if(!e||!!d.forcePlaceholderSize)b.height()||b.height(c.currentItem.innerHeight()-parseInt(c.currentItem.css("paddingTop")||0,10)-parseInt(c.currentItem.css("paddingBottom")||0,10)),b.width()||b.width(c.currentItem.innerWidth()-parseInt(c.currentItem.css("paddingLeft")||0,10)-parseInt(c.currentItem.css("paddingRight")||0,10))}}}c.placeholder=a(d.placeholder.element.call(c.element,c.currentItem)),c.currentItem.after(c.placeholder),d.placeholder.update(c,c.placeholder)},_contactContainers:function(b){var c=null,d=null;for(var e=this.containers.length-1;e>=0;e--){if(a.ui.contains(this.currentItem[0],this.containers[e].element[0]))continue;if(this._intersectsWith(this.containers[e].containerCache)){if(c&&a.ui.contains(this.containers[e].element[0],c.element[0]))continue;c=this.containers[e],d=e}else this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",b,this._uiHash(this)),this.containers[e].containerCache.over=0)}if(!!c)if(this.containers.length===1)this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1;else if(this.currentContainer!=this.containers[d]){var f=1e4,g=null,h=this.positionAbs[this.containers[d].floating?"left":"top"];for(var i=this.items.length-1;i>=0;i--){if(!a.ui.contains(this.containers[d].element[0],this.items[i].item[0]))continue;var j=this.items[i][this.containers[d].floating?"left":"top"];Math.abs(j-h)<f&&(f=Math.abs(j-h),g=this.items[i])}if(!g&&!this.options.dropOnEmpty)return;this.currentContainer=this.containers[d],g?this._rearrange(b,g,null,!0):this._rearrange(b,null,this.containers[d].element,!0),this._trigger("change",b,this._uiHash()),this.containers[d]._trigger("change",b,this._uiHash(this)),this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1}},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b,this.currentItem])):c.helper=="clone"?this.currentItem.clone():this.currentItem;d.parents("body").length||a(c.appendTo!="parent"?c.appendTo:this.currentItem[0].parentNode)[0].appendChild(d[0]),d[0]==this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(d[0].style.width==""||c.forceHelperSize)&&d.width(this.currentItem.width()),(d[0].style.height==""||c.forceHelperSize)&&d.height(this.currentItem.height());return d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)){var c=a(b.containment)[0],d=a(b.containment).offset(),e=a(c).css("overflow")!="hidden";this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(e?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(e?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName);this.cssPosition=="relative"&&(this.scrollParent[0]==document||this.scrollParent[0]==this.offsetParent[0])&&(this.offset.relative=this._getRelativeOffset());var f=b.pageX,g=b.pageY;if(this.originalPosition){this.containment&&(b.pageX-this.offset.click.left<this.containment[0]&&(f=this.containment[0]+this.offset.click.left),b.pageY-this.offset.click.top<this.containment[1]&&(g=this.containment[1]+this.offset.click.top),b.pageX-this.offset.click.left>this.containment[2]&&(f=this.containment[2]+this.offset.click.left),b.pageY-this.offset.click.top>this.containment[3]&&(g=this.containment[3]+this.offset.click.top));if(c.grid){var h=this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1];g=this.containment?h-this.offset.click.top<this.containment[1]||h-this.offset.click.top>this.containment[3]?h-this.offset.click.top<this.containment[1]?h+c.grid[1]:h-c.grid[1]:h:h;var i=this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0];f=this.containment?i-this.offset.click.left<this.containment[0]||i-this.offset.click.left>this.containment[2]?i-this.offset.click.left<this.containment[0]?i+c.grid[0]:i-c.grid[0]:i:i}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_rearrange:function(a,b,c,d){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],this.direction=="down"?b.item[0]:b.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var e=this,f=this.counter;window.setTimeout(function(){f==e.counter&&e.refreshPositions(!d)},0)},_clear:function(b,c){this.reverting=!1;var d=[],e=this;!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var f in this._storedCSS)if(this._storedCSS[f]=="auto"||this._storedCSS[f]=="static")this._storedCSS[f]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!c&&d.push(function(a){this._trigger("receive",a,this._uiHash(this.fromOutside))}),(this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!c&&d.push(function(a){this._trigger("update",a,this._uiHash())});if(!a.ui.contains(this.element[0],this.currentItem[0])){c||d.push(function(a){this._trigger("remove",a,this._uiHash())});for(var f=this.containers.length-1;f>=0;f--)a.ui.contains(this.containers[f].element[0],this.currentItem[0])&&!c&&(d.push(function(a){return function(b){a._trigger("receive",b,this._uiHash(this))}}.call(this,this.containers[f])),d.push(function(a){return function(b){a._trigger("update",b,this._uiHash(this))}}.call(this,this.containers[f])))}for(var f=this.containers.length-1;f>=0;f--)c||d.push(function(a){return function(b){a._trigger("deactivate",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over&&(d.push(function(a){return function(b){a._trigger("out",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over=0);this._storedCursor&&a("body").css("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex),this.dragging=!1;if(this.cancelHelperRemoval){if(!c){this._trigger("beforeStop",b,this._uiHash());for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}return!1}c||this._trigger("beforeStop",b,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!=this.currentItem[0]&&this.helper.remove(),this.helper=null;if(!c){for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}this.fromOutside=!1;return!0},_trigger:function(){a.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(b){var c=b||this;return{helper:c.helper,placeholder:c.placeholder||a([]),position:c.position,originalPosition:c.originalPosition,offset:c.positionAbs,item:c.currentItem,sender:b?b.element:null}}}),a.extend(a.ui.sortable,{version:"1.8.17"})})(jQuery);/*
103
+ * jQuery UI Accordion 1.8.17
104
+ *
105
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
106
+ * Dual licensed under the MIT or GPL Version 2 licenses.
107
+ * http://jquery.org/license
108
+ *
109
+ * http://docs.jquery.com/UI/Accordion
110
+ *
111
+ * Depends:
112
+ * jquery.ui.core.js
113
+ * jquery.ui.widget.js
114
+ */(function(a,b){a.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:!0,clearStyle:!1,collapsible:!1,event:"click",fillSpace:!1,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:!1,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var b=this,c=b.options;b.running=0,b.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"),b.headers=b.element.find(c.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){c.disabled||a(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){c.disabled||a(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){c.disabled||a(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){c.disabled||a(this).removeClass("ui-state-focus")}),b.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");if(c.navigation){var d=b.element.find("a").filter(c.navigationFilter).eq(0);if(d.length){var e=d.closest(".ui-accordion-header");e.length?b.active=e:b.active=d.closest(".ui-accordion-content").prev()}}b.active=b._findActive(b.active||c.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top"),b.active.next().addClass("ui-accordion-content-active"),b._createIcons(),b.resize(),b.element.attr("role","tablist"),b.headers.attr("role","tab").bind("keydown.accordion",function(a){return b._keydown(a)}).next().attr("role","tabpanel"),b.headers.not(b.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide(),b.active.length?b.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):b.headers.eq(0).attr("tabIndex",0),a.browser.safari||b.headers.find("a").attr("tabIndex",-1),c.event&&b.headers.bind(c.event.split(" ").join(".accordion ")+".accordion",function(a){b._clickHandler.call(b,a,this),a.preventDefault()})},_createIcons:function(){var b=this.options;b.icons&&(a("<span></span>").addClass("ui-icon "+b.icons.header).prependTo(this.headers),this.active.children(".ui-icon").toggleClass(b.icons.header).toggleClass(b.icons.headerSelected),this.element.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.children(".ui-icon").remove(),this.element.removeClass("ui-accordion-icons")},destroy:function(){var b=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"),this.headers.find("a").removeAttr("tabIndex"),this._destroyIcons();var c=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");(b.autoHeight||b.fillHeight)&&c.css("height","");return a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b=="active"&&this.activate(c),b=="icons"&&(this._destroyIcons(),c&&this._createIcons()),b=="disabled"&&this.headers.add(this.headers.next())[c?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(b){if(!(this.options.disabled||b.altKey||b.ctrlKey)){var c=a.ui.keyCode,d=this.headers.length,e=this.headers.index(b.target),f=!1;switch(b.keyCode){case c.RIGHT:case c.DOWN:f=this.headers[(e+1)%d];break;case c.LEFT:case c.UP:f=this.headers[(e-1+d)%d];break;case c.SPACE:case c.ENTER:this._clickHandler({target:b.target},b.target),b.preventDefault()}if(f){a(b.target).attr("tabIndex",-1),a(f).attr("tabIndex",0),f.focus();return!1}return!0}},resize:function(){var b=this.options,c;if(b.fillSpace){if(a.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}c=this.element.parent().height(),a.browser.msie&&this.element.parent().css("overflow",d),this.headers.each(function(){c-=a(this).outerHeight(!0)}),this.headers.next().each(function(){a(this).height(Math.max(0,c-a(this).innerHeight()+a(this).height()))}).css("overflow","auto")}else b.autoHeight&&(c=0,this.headers.next().each(function(){c=Math.max(c,a(this).height("").height())}).height(c));return this},activate:function(a){this.options.active=a;var b=this._findActive(a)[0];this._clickHandler({target:b},b);return this},_findActive:function(b){return b?typeof b=="number"?this.headers.filter(":eq("+b+")"):this.headers.not(this.headers.not(b)):b===!1?a([]):this.headers.filter(":eq(0)")},_clickHandler:function(b,c){var d=this.options;if(!d.disabled){if(!b.target){if(!d.collapsible)return;this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),this.active.next().addClass("ui-accordion-content-active");var e=this.active.next(),f={options:d,newHeader:a([]),oldHeader:d.active,newContent:a([]),oldContent:e},g=this.active=a([]);this._toggle(g,e,f);return}var h=a(b.currentTarget||c),i=h[0]===this.active[0];d.active=d.collapsible&&i?!1:this.headers.index(h);if(this.running||!d.collapsible&&i)return;var j=this.active,g=h.next(),e=this.active.next(),f={options:d,newHeader:i&&d.collapsible?a([]):h,oldHeader:this.active,newContent:i&&d.collapsible?a([]):g,oldContent:e},k=this.headers.index(this.active[0])>this.headers.index(h[0]);this.active=i?a([]):h,this._toggle(g,e,f,i,k),j.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),i||(h.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected),h.next().addClass("ui-accordion-content-active"));return}},_toggle:function(b,c,d,e,f){var g=this,h=g.options;g.toShow=b,g.toHide=c,g.data=d;var i=function(){if(!!g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data),g.running=c.size()===0?b.size():c.size();if(h.animated){var j={};h.collapsible&&e?j={toShow:a([]),toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace}:j={toShow:b,toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace},h.proxied||(h.proxied=h.animated),h.proxiedDuration||(h.proxiedDuration=h.duration),h.animated=a.isFunction(h.proxied)?h.proxied(j):h.proxied,h.duration=a.isFunction(h.proxiedDuration)?h.proxiedDuration(j):h.proxiedDuration;var k=a.ui.accordion.animations,l=h.duration,m=h.animated;m&&!k[m]&&!a.easing[m]&&(m="slide"),k[m]||(k[m]=function(a){this.slide(a,{easing:m,duration:l||700})}),k[m](j)}else h.collapsible&&e?b.toggle():(c.hide(),b.show()),i(!0);c.prev().attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).blur(),b.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;this.running||(this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""}),this.toHide.removeClass("ui-accordion-content-active"),this.toHide.length&&(this.toHide.parent()[0].className=this.toHide.parent()[0].className),this._trigger("change",null,this.data))}}),a.extend(a.ui.accordion,{version:"1.8.17",animations:{slide:function(b,c){b=a.extend({easing:"swing",duration:300},b,c);if(!b.toHide.size())b.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},b);else{if(!b.toShow.size()){b.toHide.animate({height:"hide",paddingTop:"hide",paddingBottom:"hide"},b);return}var d=b.toShow.css("overflow"),e=0,f={},g={},h=["height","paddingTop","paddingBottom"],i,j=b.toShow;i=j[0].style.width,j.width(j.parent().width()-parseFloat(j.css("paddingLeft"))-parseFloat(j.css("paddingRight"))-(parseFloat(j.css("borderLeftWidth"))||0)-(parseFloat(j.css("borderRightWidth"))||0)),a.each(h,function(c,d){g[d]="hide";var e=(""+a.css(b.toShow[0],d)).match(/^([\d+-.]+)(.*)$/);f[d]={value:e[1],unit:e[2]||"px"}}),b.toShow.css({height:0,overflow:"hidden"}).show(),b.toHide.filter(":hidden").each(b.complete).end().filter(":visible").animate(g,{step:function(a,c){c.prop=="height"&&(e=c.end-c.start===0?0:(c.now-c.start)/(c.end-c.start)),b.toShow[0].style[c.prop]=e*f[c.prop].value+f[c.prop].unit},duration:b.duration,easing:b.easing,complete:function(){b.autoHeight||b.toShow.css("height",""),b.toShow.css({width:i,overflow:d}),b.complete()}})}},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1e3:200})}}})})(jQuery);/*
115
+ * jQuery UI Autocomplete 1.8.17
116
+ *
117
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
118
+ * Dual licensed under the MIT or GPL Version 2 licenses.
119
+ * http://jquery.org/license
120
+ *
121
+ * http://docs.jquery.com/UI/Autocomplete
122
+ *
123
+ * Depends:
124
+ * jquery.ui.core.js
125
+ * jquery.ui.widget.js
126
+ * jquery.ui.position.js
127
+ */(function(a,b){var c=0;a.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var b=this,c=this.element[0].ownerDocument,d;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!b.options.disabled&&!b.element.propAttr("readOnly")){d=!1;var e=a.ui.keyCode;switch(c.keyCode){case e.PAGE_UP:b._move("previousPage",c);break;case e.PAGE_DOWN:b._move("nextPage",c);break;case e.UP:b._move("previous",c),c.preventDefault();break;case e.DOWN:b._move("next",c),c.preventDefault();break;case e.ENTER:case e.NUMPAD_ENTER:b.menu.active&&(d=!0,c.preventDefault());case e.TAB:if(!b.menu.active)return;b.menu.select(c);break;case e.ESCAPE:b.element.val(b.term),b.close(c);break;default:clearTimeout(b.searching),b.searching=setTimeout(function(){b.term!=b.element.val()&&(b.selectedItem=null,b.search(null,c))},b.options.delay)}}}).bind("keypress.autocomplete",function(a){d&&(d=!1,a.preventDefault())}).bind("focus.autocomplete",function(){b.options.disabled||(b.selectedItem=null,b.previous=b.element.val())}).bind("blur.autocomplete",function(a){b.options.disabled||(clearTimeout(b.searching),b.closing=setTimeout(function(){b.close(a),b._change(a)},150))}),this._initSource(),this.response=function(){return b._response.apply(b,arguments)},this.menu=a("<ul></ul>").addClass("ui-autocomplete").appendTo(a(this.options.appendTo||"body",c)[0]).mousedown(function(c){var d=b.menu.element[0];a(c.target).closest(".ui-menu-item").length||setTimeout(function(){a(document).one("mousedown",function(c){c.target!==b.element[0]&&c.target!==d&&!a.ui.contains(d,c.target)&&b.close()})},1),setTimeout(function(){clearTimeout(b.closing)},13)}).menu({focus:function(a,c){var d=c.item.data("item.autocomplete");!1!==b._trigger("focus",a,{item:d})&&/^key/.test(a.originalEvent.type)&&b.element.val(d.value)},selected:function(a,d){var e=d.item.data("item.autocomplete"),f=b.previous;b.element[0]!==c.activeElement&&(b.element.focus(),b.previous=f,setTimeout(function(){b.previous=f,b.selectedItem=e},1)),!1!==b._trigger("select",a,{item:e})&&b.element.val(e.value),b.term=b.element.val(),b.close(a),b.selectedItem=e},blur:function(a,c){b.menu.element.is(":visible")&&b.element.val()!==b.term&&b.element.val(b.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"),a.fn.bgiframe&&this.menu.element.bgiframe(),b.beforeunloadHandler=function(){b.element.removeAttr("autocomplete")},a(window).bind("beforeunload",b.beforeunloadHandler)},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"),this.menu.element.remove(),a(window).unbind("beforeunload",this.beforeunloadHandler),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b==="source"&&this._initSource(),b==="appendTo"&&this.menu.element.appendTo(a(c||"body",this.element[0].ownerDocument)[0]),b==="disabled"&&c&&this.xhr&&this.xhr.abort()},_initSource:function(){var b=this,d,e;a.isArray(this.options.source)?(d=this.options.source,this.source=function(b,c){c(a.ui.autocomplete.filter(d,b.term))}):typeof this.options.source=="string"?(e=this.options.source,this.source=function(d,f){b.xhr&&b.xhr.abort(),b.xhr=a.ajax({url:e,data:d,dataType:"json",autocompleteRequest:++c,success:function(a,b){this.autocompleteRequest===c&&f(a)},error:function(){this.autocompleteRequest===c&&f([])}})}):this.source=this.options.source},search:function(a,b){a=a!=null?a:this.element.val(),this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==!1)return this._search(a)},_search:function(a){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.source({term:a},this.response)},_response:function(a){!this.options.disabled&&a&&a.length?(a=this._normalize(a),this._suggest(a),this._trigger("open")):this.close(),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing),this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.deactivate(),this._trigger("close",a))},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(b){if(b.length&&b[0].label&&b[0].value)return b;return a.map(b,function(b){if(typeof b=="string")return{label:b,value:b};return a.extend({label:b.label||b.value,value:b.value||b.label},b)})},_suggest:function(b){var c=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(c,b),this.menu.deactivate(),this.menu.refresh(),c.show(),this._resizeMenu(),c.position(a.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next(new a.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(b,c){var d=this;a.each(c,function(a,c){d._renderItem(b,c)})},_renderItem:function(b,c){return a("<li></li>").data("item.autocomplete",c).append(a("<a></a>").text(c.label)).appendTo(b)},_move:function(a,b){if(!this.menu.element.is(":visible"))this.search(null,b);else{if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term),this.menu.deactivate();return}this.menu[a](b)}},widget:function(){return this.menu.element}}),a.extend(a.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")},filter:function(b,c){var d=new RegExp(a.ui.autocomplete.escapeRegex(c),"i");return a.grep(b,function(a){return d.test(a.label||a.value||a)})}})})(jQuery),function(a){a.widget("ui.menu",{_create:function(){var b=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(c){!a(c.target).closest(".ui-menu-item a").length||(c.preventDefault(),b.select(c))}),this.refresh()},refresh:function(){var b=this,c=this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem");c.children("a").addClass("ui-corner-all").attr("tabindex",-1).mouseenter(function(c){b.activate(c,a(this).parent())}).mouseleave(function(){b.deactivate()})},activate:function(a,b){this.deactivate();if(this.hasScroll()){var c=b.offset().top-this.element.offset().top,d=this.element.scrollTop(),e=this.element.height();c<0?this.element.scrollTop(d+c):c>=e&&this.element.scrollTop(d+c-e+b.height())}this.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end(),this._trigger("focus",a,{item:b})},deactivate:function(){!this.active||(this.active.children("a").removeClass("ui-state-hover").removeAttr("id"),this._trigger("blur"),this.active=null)},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(!this.active)this.activate(c,this.element.children(b));else{var d=this.active[a+"All"](".ui-menu-item").eq(0);d.length?this.activate(c,d):this.activate(c,this.element.children(b))}},nextPage:function(b){if(this.hasScroll()){if(!this.active||this.last()){this.activate(b,this.element.children(".ui-menu-item:first"));return}var c=this.active.offset().top,d=this.element.height(),e=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c-d+a(this).height();return b<10&&b>-10});e.length||(e=this.element.children(".ui-menu-item:last")),this.activate(b,e)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(b){if(this.hasScroll()){if(!this.active||this.first()){this.activate(b,this.element.children(".ui-menu-item:last"));return}var c=this.active.offset().top,d=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c+d-a(this).height();return b<10&&b>-10}),result.length||(result=this.element.children(".ui-menu-item:first")),this.activate(b,result)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[a.fn.prop?"prop":"attr"]("scrollHeight")},select:function(a){this._trigger("selected",a,{item:this.active})}})}(jQuery);/*
128
+ * jQuery UI Button 1.8.17
129
+ *
130
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
131
+ * Dual licensed under the MIT or GPL Version 2 licenses.
132
+ * http://jquery.org/license
133
+ *
134
+ * http://docs.jquery.com/UI/Button
135
+ *
136
+ * Depends:
137
+ * jquery.ui.core.js
138
+ * jquery.ui.widget.js
139
+ */(function(a,b){var c,d,e,f,g="ui-button ui-widget ui-state-default ui-corner-all",h="ui-state-hover ui-state-active ",i="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",j=function(){var b=a(this).find(":ui-button");setTimeout(function(){b.button("refresh")},1)},k=function(b){var c=b.name,d=b.form,e=a([]);c&&(d?e=a(d).find("[name='"+c+"']"):e=a("[name='"+c+"']",b.ownerDocument).filter(function(){return!this.form}));return e};a.widget("ui.button",{options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",j),typeof this.options.disabled!="boolean"&&(this.options.disabled=this.element.propAttr("disabled")),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var b=this,h=this.options,i=this.type==="checkbox"||this.type==="radio",l="ui-state-hover"+(i?"":" ui-state-active"),m="ui-state-focus";h.label===null&&(h.label=this.buttonElement.html()),this.element.is(":disabled")&&(h.disabled=!0),this.buttonElement.addClass(g).attr("role","button").bind("mouseenter.button",function(){h.disabled||(a(this).addClass("ui-state-hover"),this===c&&a(this).addClass("ui-state-active"))}).bind("mouseleave.button",function(){h.disabled||a(this).removeClass(l)}).bind("click.button",function(a){h.disabled&&(a.preventDefault(),a.stopImmediatePropagation())}),this.element.bind("focus.button",function(){b.buttonElement.addClass(m)}).bind("blur.button",function(){b.buttonElement.removeClass(m)}),i&&(this.element.bind("change.button",function(){f||b.refresh()}),this.buttonElement.bind("mousedown.button",function(a){h.disabled||(f=!1,d=a.pageX,e=a.pageY)}).bind("mouseup.button",function(a){!h.disabled&&(d!==a.pageX||e!==a.pageY)&&(f=!0)})),this.type==="checkbox"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).toggleClass("ui-state-active"),b.buttonElement.attr("aria-pressed",b.element[0].checked)}):this.type==="radio"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).addClass("ui-state-active"),b.buttonElement.attr("aria-pressed","true");var c=b.element[0];k(c).not(c).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown.button",function(){if(h.disabled)return!1;a(this).addClass("ui-state-active"),c=this,a(document).one("mouseup",function(){c=null})}).bind("mouseup.button",function(){if(h.disabled)return!1;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(b){if(h.disabled)return!1;(b.keyCode==a.ui.keyCode.SPACE||b.keyCode==a.ui.keyCode.ENTER)&&a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(b){b.keyCode===a.ui.keyCode.SPACE&&a(this).click()})),this._setOption("disabled",h.disabled),this._resetButton()},_determineButtonType:function(){this.element.is(":checkbox")?this.type="checkbox":this.element.is(":radio")?this.type="radio":this.element.is("input")?this.type="input":this.type="button";if(this.type==="checkbox"||this.type==="radio"){var a=this.element.parents().filter(":last"),b="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(b),this.buttonElement.length||(a=a.length?a.siblings():this.element.siblings(),this.buttonElement=a.filter(b),this.buttonElement.length||(this.buttonElement=a.find(b))),this.element.addClass("ui-helper-hidden-accessible");var c=this.element.is(":checked");c&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.attr("aria-pressed",c)}else this.buttonElement=this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(g+" "+h+" "+i).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title"),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);b==="disabled"?c?this.element.propAttr("disabled",!0):this.element.propAttr("disabled",!1):this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b),this.type==="radio"?k(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):this.type==="checkbox"&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var b=this.buttonElement.removeClass(i),c=a("<span></span>",this.element[0].ownerDocument).addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary,f=[];d.primary||d.secondary?(this.options.text&&f.push("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary")),d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>"),d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>"),this.options.text||(f.push(e?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||b.attr("title",c))):f.push("ui-button-text-only"),b.addClass(f.join(" "))}}}),a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c),a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var b=this.element.css("direction")==="rtl";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(b?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(b?"ui-corner-left":"ui-corner-right").end().end()},destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"),a.Widget.prototype.destroy.call(this)}})})(jQuery);/*
140
+ * jQuery UI Dialog 1.8.17
141
+ *
142
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
143
+ * Dual licensed under the MIT or GPL Version 2 licenses.
144
+ * http://jquery.org/license
145
+ *
146
+ * http://docs.jquery.com/UI/Dialog
147
+ *
148
+ * Depends:
149
+ * jquery.ui.core.js
150
+ * jquery.ui.widget.js
151
+ * jquery.ui.button.js
152
+ * jquery.ui.draggable.js
153
+ * jquery.ui.mouse.js
154
+ * jquery.ui.position.js
155
+ * jquery.ui.resizable.js
156
+ */(function(a,b){var c="ui-dialog ui-widget ui-widget-content ui-corner-all ",d={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},e={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},f=a.attrFn||{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0,click:!0};a.widget("ui.dialog",{options:{autoOpen:!0,buttons:{},closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:!1,maxWidth:!1,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",collision:"fit",using:function(b){var c=a(this).css(b).offset().top;c<0&&a(this).css("top",b.top-c)}},resizable:!0,show:null,stack:!0,title:"",width:300,zIndex:1e3},_create:function(){this.originalTitle=this.element.attr("title"),typeof this.originalTitle!="string"&&(this.originalTitle=""),this.options.title=this.options.title||this.originalTitle;var b=this,d=b.options,e=d.title||"&#160;",f=a.ui.dialog.getTitleId(b.element),g=(b.uiDialog=a("<div></div>")).appendTo(document.body).hide().addClass(c+d.dialogClass).css({zIndex:d.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(c){d.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}).attr({role:"dialog","aria-labelledby":f}).mousedown(function(a){b.moveToTop(!1,a)}),h=b.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g),i=(b.uiDialogTitlebar=a("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),j=a('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){j.addClass("ui-state-hover")},function(){j.removeClass("ui-state-hover")}).focus(function(){j.addClass("ui-state-focus")}).blur(function(){j.removeClass("ui-state-focus")}).click(function(a){b.close(a);return!1}).appendTo(i),k=(b.uiDialogTitlebarCloseText=a("<span></span>")).addClass("ui-icon ui-icon-closethick").text(d.closeText).appendTo(j),l=a("<span></span>").addClass("ui-dialog-title").attr("id",f).html(e).prependTo(i);a.isFunction(d.beforeclose)&&!a.isFunction(d.beforeClose)&&(d.beforeClose=d.beforeclose),i.find("*").add(i).disableSelection(),d.draggable&&a.fn.draggable&&b._makeDraggable(),d.resizable&&a.fn.resizable&&b._makeResizable(),b._createButtons(d.buttons),b._isOpen=!1,a.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy(),a.uiDialog.hide(),a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"),a.uiDialog.remove(),a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(b){var c=this,d,e;if(!1!==c._trigger("beforeClose",b)){c.overlay&&c.overlay.destroy(),c.uiDialog.unbind("keypress.ui-dialog"),c._isOpen=!1,c.options.hide?c.uiDialog.hide(c.options.hide,function(){c._trigger("close",b)}):(c.uiDialog.hide(),c._trigger("close",b)),a.ui.dialog.overlay.resize(),c.options.modal&&(d=0,a(".ui-dialog").each(function(){this!==c.uiDialog[0]&&(e=a(this).css("z-index"),isNaN(e)||(d=Math.max(d,e)))}),a.ui.dialog.maxZ=d);return c}},isOpen:function(){return this._isOpen},moveToTop:function(b,c){var d=this,e=d.options,f;if(e.modal&&!b||!e.stack&&!e.modal)return d._trigger("focus",c);e.zIndex>a.ui.dialog.maxZ&&(a.ui.dialog.maxZ=e.zIndex),d.overlay&&(a.ui.dialog.maxZ+=1,d.overlay.$el.css("z-index",a.ui.dialog.overlay.maxZ=a.ui.dialog.maxZ)),f={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()},a.ui.dialog.maxZ+=1,d.uiDialog.css("z-index",a.ui.dialog.maxZ),d.element.attr(f),d._trigger("focus",c);return d},open:function(){if(!this._isOpen){var b=this,c=b.options,d=b.uiDialog;b.overlay=c.modal?new a.ui.dialog.overlay(b):null,b._size(),b._position(c.position),d.show(c.show),b.moveToTop(!0),c.modal&&d.bind("keydown.ui-dialog",function(b){if(b.keyCode===a.ui.keyCode.TAB){var c=a(":tabbable",this),d=c.filter(":first"),e=c.filter(":last");if(b.target===e[0]&&!b.shiftKey){d.focus(1);return!1}if(b.target===d[0]&&b.shiftKey){e.focus(1);return!1}}}),a(b.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus(),b._isOpen=!0,b._trigger("open");return b}},_createButtons:function(b){var c=this,d=!1,e=a("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=a("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);c.uiDialog.find(".ui-dialog-buttonpane").remove(),typeof b=="object"&&b!==null&&a.each(b,function(){return!(d=!0)}),d&&(a.each(b,function(b,d){d=a.isFunction(d)?{click:d,text:b}:d;var e=a('<button type="button"></button>').click(function(){d.click.apply(c.element[0],arguments)}).appendTo(g);a.each(d,function(a,b){a!=="click"&&(a in f?e[a](b):e.attr(a,b))}),a.fn.button&&e.button()}),e.appendTo(c.uiDialog))},_makeDraggable:function(){function f(a){return{position:a.position,offset:a.offset}}var b=this,c=b.options,d=a(document),e;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(d,g){e=c.height==="auto"?"auto":a(this).height(),a(this).height(a(this).height()).addClass("ui-dialog-dragging"),b._trigger("dragStart",d,f(g))},drag:function(a,c){b._trigger("drag",a,f(c))},stop:function(g,h){c.position=[h.position.left-d.scrollLeft(),h.position.top-d.scrollTop()],a(this).removeClass("ui-dialog-dragging").height(e),b._trigger("dragStop",g,f(h)),a.ui.dialog.overlay.resize()}})},_makeResizable:function(c){function h(a){return{originalPosition:a.originalPosition,originalSize:a.originalSize,position:a.position,size:a.size}}c=c===b?this.options.resizable:c;var d=this,e=d.options,f=d.uiDialog.css("position"),g=typeof c=="string"?c:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:g,start:function(b,c){a(this).addClass("ui-dialog-resizing"),d._trigger("resizeStart",b,h(c))},resize:function(a,b){d._trigger("resize",a,h(b))},stop:function(b,c){a(this).removeClass("ui-dialog-resizing"),e.height=a(this).height(),e.width=a(this).width(),d._trigger("resizeStop",b,h(c)),a.ui.dialog.overlay.resize()}}).css("position",f).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(b){var c=[],d=[0,0],e;if(b){if(typeof b=="string"||typeof b=="object"&&"0"in b)c=b.split?b.split(" "):[b[0],b[1]],c.length===1&&(c[1]=c[0]),a.each(["left","top"],function(a,b){+c[a]===c[a]&&(d[a]=c[a],c[a]=b)}),b={my:c.join(" "),at:c.join(" "),offset:d.join(" ")};b=a.extend({},a.ui.dialog.prototype.options.position,b)}else b=a.ui.dialog.prototype.options.position;e=this.uiDialog.is(":visible"),e||this.uiDialog.show(),this.uiDialog.css({top:0,left:0}).position(a.extend({of:window},b)),e||this.uiDialog.hide()},_setOptions:function(b){var c=this,f={},g=!1;a.each(b,function(a,b){c._setOption(a,b),a in d&&(g=!0),a in e&&(f[a]=b)}),g&&this._size(),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",f)},_setOption:function(b,d){var e=this,f=e.uiDialog;switch(b){case"beforeclose":b="beforeClose";break;case"buttons":e._createButtons(d);break;case"closeText":e.uiDialogTitlebarCloseText.text(""+d);break;case"dialogClass":f.removeClass(e.options.dialogClass).addClass(c+d);break;case"disabled":d?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case"draggable":var g=f.is(":data(draggable)");g&&!d&&f.draggable("destroy"),!g&&d&&e._makeDraggable();break;case"position":e._position(d);break;case"resizable":var h=f.is(":data(resizable)");h&&!d&&f.resizable("destroy"),h&&typeof d=="string"&&f.resizable("option","handles",d),!h&&d!==!1&&e._makeResizable(d);break;case"title":a(".ui-dialog-title",e.uiDialogTitlebar).html(""+(d||"&#160;"))}a.Widget.prototype._setOption.apply(e,arguments)},_size:function(){var b=this.options,c,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0}),b.minWidth>b.width&&(b.width=b.minWidth),c=this.uiDialog.css({height:"auto",width:b.width}).height(),d=Math.max(0,b.minHeight-c);if(b.height==="auto")if(a.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();var f=this.element.css("height","auto").height();e||this.uiDialog.hide(),this.element.height(Math.max(f,d))}else this.element.height(Math.max(b.height-c,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}}),a.extend(a.ui.dialog,{version:"1.8.17",uuid:0,maxZ:0,getTitleId:function(a){var b=a.attr("id");b||(this.uuid+=1,b=this.uuid);return"ui-dialog-title-"+b},overlay:function(b){this.$el=a.ui.dialog.overlay.create(b)}}),a.extend(a.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:a.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(b){this.instances.length===0&&(setTimeout(function(){a.ui.dialog.overlay.instances.length&&a(document).bind(a.ui.dialog.overlay.events,function(b){if(a(b.target).zIndex()<a.ui.dialog.overlay.maxZ)return!1})},1),a(document).bind("keydown.dialog-overlay",function(c){b.options.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}),a(window).bind("resize.dialog-overlay",a.ui.dialog.overlay.resize));var c=(this.oldInstances.pop()||a("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});a.fn.bgiframe&&c.bgiframe(),this.instances.push(c);return c},destroy:function(b){var c=a.inArray(b,this.instances);c!=-1&&this.oldInstances.push(this.instances.splice(c,1)[0]),this.instances.length===0&&a([document,window]).unbind(".dialog-overlay"),b.remove();var d=0;a.each(this.instances,function(){d=Math.max(d,this.css("z-index"))}),this.maxZ=d},height:function(){var b,c;if(a.browser.msie&&a.browser.version<7){b=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),c=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return b<c?a(window).height()+"px":b+"px"}return a(document).height()+"px"},width:function(){var b,c;if(a.browser.msie){b=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth),c=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return b<c?a(window).width()+"px":b+"px"}return a(document).width()+"px"},resize:function(){var b=a([]);a.each(a.ui.dialog.overlay.instances,function(){b=b.add(this)}),b.css({width:0,height:0}).css({width:a.ui.dialog.overlay.width(),height:a.ui.dialog.overlay.height()})}}),a.extend(a.ui.dialog.overlay.prototype,{destroy:function(){a.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);/*
157
+ * jQuery UI Slider 1.8.17
158
+ *
159
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
160
+ * Dual licensed under the MIT or GPL Version 2 licenses.
161
+ * http://jquery.org/license
162
+ *
163
+ * http://docs.jquery.com/UI/Slider
164
+ *
165
+ * Depends:
166
+ * jquery.ui.core.js
167
+ * jquery.ui.mouse.js
168
+ * jquery.ui.widget.js
169
+ */(function(a,b){var c=5;a.widget("ui.slider",a.ui.mouse,{widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null},_create:function(){var b=this,d=this.options,e=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",g=d.values&&d.values.length||1,h=[];this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"+(d.disabled?" ui-slider-disabled ui-disabled":"")),this.range=a([]),d.range&&(d.range===!0&&(d.values||(d.values=[this._valueMin(),this._valueMin()]),d.values.length&&d.values.length!==2&&(d.values=[d.values[0],d.values[0]])),this.range=a("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(d.range==="min"||d.range==="max"?" ui-slider-range-"+d.range:"")));for(var i=e.length;i<g;i+=1)h.push(f);this.handles=e.add(a(h.join("")).appendTo(b.element)),this.handle=this.handles.eq(0),this.handles.add(this.range).filter("a").click(function(a){a.preventDefault()}).hover(function(){d.disabled||a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){d.disabled?a(this).blur():(a(".ui-slider .ui-state-focus").removeClass("ui-state-focus"),a(this).addClass("ui-state-focus"))}).blur(function(){a(this).removeClass("ui-state-focus")}),this.handles.each(function(b){a(this).data("index.ui-slider-handle",b)}),this.handles.keydown(function(d){var e=!0,f=a(this).data("index.ui-slider-handle"),g,h,i,j;if(!b.options.disabled){switch(d.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.PAGE_UP:case a.ui.keyCode.PAGE_DOWN:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:e=!1;if(!b._keySliding){b._keySliding=!0,a(this).addClass("ui-state-active"),g=b._start(d,f);if(g===!1)return}}j=b.options.step,b.options.values&&b.options.values.length?h=i=b.values(f):h=i=b.value();switch(d.keyCode){case a.ui.keyCode.HOME:i=b._valueMin();break;case a.ui.keyCode.END:i=b._valueMax();break;case a.ui.keyCode.PAGE_UP:i=b._trimAlignValue(h+(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.PAGE_DOWN:i=b._trimAlignValue(h-(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(h===b._valueMax())return;i=b._trimAlignValue(h+j);break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(h===b._valueMin())return;i=b._trimAlignValue(h-j)}b._slide(d,f,i);return e}}).keyup(function(c){var d=a(this).data("index.ui-slider-handle");b._keySliding&&(b._keySliding=!1,b._stop(c,d),b._change(c,d),a(this).removeClass("ui-state-active"))}),this._refreshValue(),this._animateOff=!1},destroy:function(){this.handles.remove(),this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"),this._mouseDestroy();return this},_mouseCapture:function(b){var c=this.options,d,e,f,g,h,i,j,k,l;if(c.disabled)return!1;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),d={x:b.pageX,y:b.pageY},e=this._normValueFromMouse(d),f=this._valueMax()-this._valueMin()+1,h=this,this.handles.each(function(b){var c=Math.abs(e-h.values(b));f>c&&(f=c,g=a(this),i=b)}),c.range===!0&&this.values(1)===c.min&&(i+=1,g=a(this.handles[i])),j=this._start(b,i);if(j===!1)return!1;this._mouseSliding=!0,h._handleIndex=i,g.addClass("ui-state-active").focus(),k=g.offset(),l=!a(b.target).parents().andSelf().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:b.pageX-k.left-g.width()/2,top:b.pageY-k.top-g.height()/2-(parseInt(g.css("borderTopWidth"),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(b,i,e),this._animateOff=!0;return!0},_mouseStart:function(a){return!0},_mouseDrag:function(a){var b={x:a.pageX,y:a.pageY},c=this._normValueFromMouse(b);this._slide(a,this._handleIndex,c);return!1},_mouseStop:function(a){this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(a,this._handleIndex),this._change(a,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1;return!1},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b,c,d,e,f;this.orientation==="horizontal"?(b=this.elementSize.width,c=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(b=this.elementSize.height,c=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),d=c/b,d>1&&(d=1),d<0&&(d=0),this.orientation==="vertical"&&(d=1-d),e=this._valueMax()-this._valueMin(),f=this._valueMin()+d*e;return this._trimAlignValue(f)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values());return this._trigger("start",a,c)},_slide:function(a,b,c){var d,e,f;this.options.values&&this.options.values.length?(d=this.values(b?0:1),this.options.values.length===2&&this.options.range===!0&&(b===0&&c>d||b===1&&c<d)&&(c=d),c!==this.values(b)&&(e=this.values(),e[b]=c,f=this._trigger("slide",a,{handle:this.handles[b],value:c,values:e}),d=this.values(b?0:1),f!==!1&&this.values(b,c,!0))):c!==this.value()&&(f=this._trigger("slide",a,{handle:this.handles[b],value:c}),f!==!1&&this.value(c))},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("change",a,c)}},value:function(a){if(arguments.length)this.options.value=this._trimAlignValue(a),this._refreshValue(),this._change(null,0);else return this._value()},values:function(b,c){var d,e,f;if(arguments.length>1)this.options.values[b]=this._trimAlignValue(c),this._refreshValue(),this._change(null,b);else{if(!arguments.length)return this._values();if(!a.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(b):this.value();d=this.options.values,e=arguments[0];for(f=0;f<d.length;f+=1)d[f]=this._trimAlignValue(e[f]),this._change(null,f);this._refreshValue()}},_setOption:function(b,c){var d,e=0;a.isArray(this.options.values)&&(e=this.options.values.length),a.Widget.prototype._setOption.apply(this,arguments);switch(b){case"disabled":c?(this.handles.filter(".ui-state-focus").blur(),this.handles.removeClass("ui-state-hover"),this.handles.propAttr("disabled",!0),this.element.addClass("ui-disabled")):(this.handles.propAttr("disabled",!1),this.element.removeClass("ui-disabled"));break;case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":this._animateOff=!0,this._refreshValue();for(d=0;d<e;d+=1)this._change(null,d);this._animateOff=!1}},_value:function(){var a=this.options.value;a=this._trimAlignValue(a);return a},_values:function(a){var b,c,d;if(arguments.length){b=this.options.values[a],b=this._trimAlignValue(b);return b}c=this.options.values.slice();for(d=0;d<c.length;d+=1)c[d]=this._trimAlignValue(c[d]);return c},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b,d=a-c;Math.abs(c)*2>=b&&(d+=c>0?b:-b);return parseFloat(d.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var b=this.options.range,c=this.options,d=this,e=this._animateOff?!1:c.animate,f,g={},h,i,j,k;this.options.values&&this.options.values.length?this.handles.each(function(b,i){f=(d.values(b)-d._valueMin())/(d._valueMax()-d._valueMin())*100,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",a(this).stop(1,1)[e?"animate":"css"](g,c.animate),d.options.range===!0&&(d.orientation==="horizontal"?(b===0&&d.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({width:f-h+"%"},{queue:!1,duration:c.animate})):(b===0&&d.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({height:f-h+"%"},{queue:!1,duration:c.animate}))),h=f}):(i=this.value(),j=this._valueMin(),k=this._valueMax(),f=k!==j?(i-j)/(k-j)*100:0,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",this.handle.stop(1,1)[e?"animate":"css"](g,c.animate),b==="min"&&this.orientation==="horizontal"&&this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"},c.animate),b==="max"&&this.orientation==="horizontal"&&this.range[e?"animate":"css"]({width:100-f+"%"},{queue:!1,duration:c.animate}),b==="min"&&this.orientation==="vertical"&&this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},c.animate),b==="max"&&this.orientation==="vertical"&&this.range[e?"animate":"css"]({height:100-f+"%"},{queue:!1,duration:c.animate}))}}),a.extend(a.ui.slider,{version:"1.8.17"})})(jQuery);/*
170
+ * jQuery UI Tabs 1.8.17
171
+ *
172
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
173
+ * Dual licensed under the MIT or GPL Version 2 licenses.
174
+ * http://jquery.org/license
175
+ *
176
+ * http://docs.jquery.com/UI/Tabs
177
+ *
178
+ * Depends:
179
+ * jquery.ui.core.js
180
+ * jquery.ui.widget.js
181
+ */(function(a,b){function f(){return++d}function e(){return++c}var c=0,d=0;a.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:!1,cookie:null,collapsible:!1,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(!0)},_setOption:function(a,b){if(a=="selected"){if(this.options.collapsible&&b==this.options.selected)return;this.select(b)}else this.options[a]=b,this._tabify()},_tabId:function(a){return a.title&&a.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+e()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+f());return a.cookie.apply(null,[b].concat(a.makeArray(arguments)))},_ui:function(a,b){return{tab:a,panel:b,index:this.anchors.index(a)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=a(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(c){function m(b,c){b.css("display",""),!a.support.opacity&&c.opacity&&b[0].style.removeAttribute("filter")}var d=this,e=this.options,f=/^#.+/;this.list=this.element.find("ol,ul").eq(0),this.lis=a(" > li:has(a[href])",this.list),this.anchors=this.lis.map(function(){return a("a",this)[0]}),this.panels=a([]),this.anchors.each(function(b,c){var g=a(c).attr("href"),h=g.split("#")[0],i;h&&(h===location.toString().split("#")[0]||(i=a("base")[0])&&h===i.href)&&(g=c.hash,c.href=g);if(f.test(g))d.panels=d.panels.add(d.element.find(d._sanitizeSelector(g)));else if(g&&g!=="#"){a.data(c,"href.tabs",g),a.data(c,"load.tabs",g.replace(/#.*$/,""));var j=d._tabId(c);c.href="#"+j;var k=d.element.find("#"+j);k.length||(k=a(e.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(d.panels[b-1]||d.list),k.data("destroy.tabs",!0)),d.panels=d.panels.add(k)}else e.disabled.push(b)}),c?(this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"),this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.lis.addClass("ui-state-default ui-corner-top"),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom"),e.selected===b?(location.hash&&this.anchors.each(function(a,b){if(b.hash==location.hash){e.selected=a;return!1}}),typeof e.selected!="number"&&e.cookie&&(e.selected=parseInt(d._cookie(),10)),typeof e.selected!="number"&&this.lis.filter(".ui-tabs-selected").length&&(e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"))),e.selected=e.selected||(this.lis.length?0:-1)):e.selected===null&&(e.selected=-1),e.selected=e.selected>=0&&this.anchors[e.selected]||e.selected<0?e.selected:0,e.disabled=a.unique(e.disabled.concat(a.map(this.lis.filter(".ui-state-disabled"),function(a,b){return d.lis.index(a)}))).sort(),a.inArray(e.selected,e.disabled)!=-1&&e.disabled.splice(a.inArray(e.selected,e.disabled),1),this.panels.addClass("ui-tabs-hide"),this.lis.removeClass("ui-tabs-selected ui-state-active"),e.selected>=0&&this.anchors.length&&(d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash)).removeClass("ui-tabs-hide"),this.lis.eq(e.selected).addClass("ui-tabs-selected ui-state-active"),d.element.queue("tabs",function(){d._trigger("show",null,d._ui(d.anchors[e.selected],d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash))[0]))}),this.load(e.selected)),a(window).bind("unload",function(){d.lis.add(d.anchors).unbind(".tabs"),d.lis=d.anchors=d.panels=null})):e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")),this.element[e.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible"),e.cookie&&this._cookie(e.selected,e.cookie);for(var g=0,h;h=this.lis[g];g++)a(h)[a.inArray(g,e.disabled)!=-1&&!a(h).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");e.cache===!1&&this.anchors.removeData("cache.tabs"),this.lis.add(this.anchors).unbind(".tabs");if(e.event!=="mouseover"){var i=function(a,b){b.is(":not(.ui-state-disabled)")&&b.addClass("ui-state-"+a)},j=function(a,b){b.removeClass("ui-state-"+a)};this.lis.bind("mouseover.tabs",function(){i("hover",a(this))}),this.lis.bind("mouseout.tabs",function(){j("hover",a(this))}),this.anchors.bind("focus.tabs",function(){i("focus",a(this).closest("li"))}),this.anchors.bind("blur.tabs",function(){j("focus",a(this).closest("li"))})}var k,l;e.fx&&(a.isArray(e.fx)?(k=e.fx[0],l=e.fx[1]):k=l=e.fx);var n=l?function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.hide().removeClass("ui-tabs-hide").animate(l,l.duration||"normal",function(){m(c,l),d._trigger("show",null,d._ui(b,c[0]))})}:function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.removeClass("ui-tabs-hide"),d._trigger("show",null,d._ui(b,c[0]))},o=k?function(a,b){b.animate(k,k.duration||"normal",function(){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),m(b,k),d.element.dequeue("tabs")})}:function(a,b,c){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),d.element.dequeue("tabs")};this.anchors.bind(e.event+".tabs",function(){var b=this,c=a(b).closest("li"),f=d.panels.filter(":not(.ui-tabs-hide)"),g=d.element.find(d._sanitizeSelector(b.hash));if(c.hasClass("ui-tabs-selected")&&!e.collapsible||c.hasClass("ui-state-disabled")||c.hasClass("ui-state-processing")||d.panels.filter(":animated").length||d._trigger("select",null,d._ui(this,g[0]))===!1){this.blur();return!1}e.selected=d.anchors.index(this),d.abort();if(e.collapsible){if(c.hasClass("ui-tabs-selected")){e.selected=-1,e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){o(b,f)}).dequeue("tabs"),this.blur();return!1}if(!f.length){e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this)),this.blur();return!1}}e.cookie&&d._cookie(e.selected,e.cookie);if(g.length)f.length&&d.element.queue("tabs",function(){o(b,f)}),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this));else throw"jQuery UI Tabs: Mismatching fragment identifier.";a.browser.msie&&this.blur()}),this.anchors.bind("click.tabs",function(){return!1})},_getIndex:function(a){typeof a=="string"&&(a=this.anchors.index(this.anchors.filter("[href$="+a+"]")));return a},destroy:function(){var b=this.options;this.abort(),this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs"),this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.anchors.each(function(){var b=a.data(this,"href.tabs");b&&(this.href=b);var c=a(this).unbind(".tabs");a.each(["href","load","cache"],function(a,b){c.removeData(b+".tabs")})}),this.lis.unbind(".tabs").add(this.panels).each(function(){a.data(this,"destroy.tabs")?a(this).remove():a(this).removeClass(["ui-state-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-content","ui-corner-bottom","ui-tabs-hide"].join(" "))}),b.cookie&&this._cookie(null,b.cookie);return this},add:function(c,d,e){e===b&&(e=this.anchors.length);var f=this,g=this.options,h=a(g.tabTemplate.replace(/#\{href\}/g,c).replace(/#\{label\}/g,d)),i=c.indexOf("#")?this._tabId(a("a",h)[0]):c.replace("#","");h.addClass("ui-state-default ui-corner-top").data("destroy.tabs",!0);var j=f.element.find("#"+i);j.length||(j=a(g.panelTemplate).attr("id",i).data("destroy.tabs",!0)),j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide"),e>=this.lis.length?(h.appendTo(this.list),j.appendTo(this.list[0].parentNode)):(h.insertBefore(this.lis[e]),j.insertBefore(this.panels[e])),g.disabled=a.map(g.disabled,function(a,b){return a>=e?++a:a}),this._tabify(),this.anchors.length==1&&(g.selected=0,h.addClass("ui-tabs-selected ui-state-active"),j.removeClass("ui-tabs-hide"),this.element.queue("tabs",function(){f._trigger("show",null,f._ui(f.anchors[0],f.panels[0]))}),this.load(0)),this._trigger("add",null,this._ui(this.anchors[e],this.panels[e]));return this},remove:function(b){b=this._getIndex(b);var c=this.options,d=this.lis.eq(b).remove(),e=this.panels.eq(b).remove();d.hasClass("ui-tabs-selected")&&this.anchors.length>1&&this.select(b+(b+1<this.anchors.length?1:-1)),c.disabled=a.map(a.grep(c.disabled,function(a,c){return a!=b}),function(a,c){return a>=b?--a:a}),this._tabify(),this._trigger("remove",null,this._ui(d.find("a")[0],e[0]));return this},enable:function(b){b=this._getIndex(b);var c=this.options;if(a.inArray(b,c.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled"),c.disabled=a.grep(c.disabled,function(a,c){return a!=b}),this._trigger("enable",null,this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(a){a=this._getIndex(a);var b=this,c=this.options;a!=c.selected&&(this.lis.eq(a).addClass("ui-state-disabled"),c.disabled.push(a),c.disabled.sort(),this._trigger("disable",null,this._ui(this.anchors[a],this.panels[a])));return this},select:function(a){a=this._getIndex(a);if(a==-1)if(this.options.collapsible&&this.options.selected!=-1)a=this.options.selected;else return this;this.anchors.eq(a).trigger(this.options.event+".tabs");return this},load:function(b){b=this._getIndex(b);var c=this,d=this.options,e=this.anchors.eq(b)[0],f=a.data(e,"load.tabs");this.abort();if(!f||this.element.queue("tabs").length!==0&&a.data(e,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(d.spinner){var g=a("span",e);g.data("label.tabs",g.html()).html(d.spinner)}this.xhr=a.ajax(a.extend({},d.ajaxOptions,{url:f,success:function(f,g){c.element.find(c._sanitizeSelector(e.hash)).html(f),c._cleanup(),d.cache&&a.data(e,"cache.tabs",!0),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.success(f,g)}catch(h){}},error:function(a,f,g){c._cleanup(),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.error(a,f,b,e)}catch(g){}}})),c.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]),this.panels.stop(!1,!0),this.element.queue("tabs",this.element.queue("tabs").splice(-2,2)),this.xhr&&(this.xhr.abort(),delete this.xhr),this._cleanup();return this},url:function(a,b){this.anchors.eq(a).removeData("cache.tabs").data("load.tabs",b);return this},length:function(){return this.anchors.length}}),a.extend(a.ui.tabs,{version:"1.8.17"}),a.extend(a.ui.tabs.prototype,{rotation:null,rotate:function(a,b){var c=this,d=this.options,e=c._rotate||(c._rotate=function(b){clearTimeout(c.rotation),c.rotation=setTimeout(function(){var a=d.selected;c.select(++a<c.anchors.length?a:0)},a),b&&b.stopPropagation()}),f=c._unrotate||(c._unrotate=b?function(a){t=d.selected,e()}:function(a){a.clientX&&c.rotate(null)});a?(this.element.bind("tabsshow",e),this.anchors.bind(d.event+".tabs",f),e()):(clearTimeout(c.rotation),this.element.unbind("tabsshow",e),this.anchors.unbind(d.event+".tabs",f),delete this._rotate,delete this._unrotate);return this}})})(jQuery);/*
182
+ * jQuery UI Datepicker 1.8.17
183
+ *
184
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
185
+ * Dual licensed under the MIT or GPL Version 2 licenses.
186
+ * http://jquery.org/license
187
+ *
188
+ * http://docs.jquery.com/UI/Datepicker
189
+ *
190
+ * Depends:
191
+ * jquery.ui.core.js
192
+ */(function($,undefined){function isArray(a){return a&&($.browser.safari&&typeof a=="object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(\)/))}function extendRemove(a,b){$.extend(a,b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}function bindHover(a){var b="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout",function(a){var c=$(a.target).closest(b);!c.length||c.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(c){var d=$(c.target).closest(b);!$.datepicker._isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])&&!!d.length&&(d.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-prev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-next")&&d.addClass("ui-datepicker-next-hover"))})}function Datepicker(){this.debug=!1,this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},$.extend(this._defaults,this.regional[""]),this.dpDiv=bindHover($('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}$.extend($.ui,{datepicker:{version:"1.8.17"}});var PROP_NAME="datepicker",dpuuid=(new Date).getTime(),instActive;$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){extendRemove(this._defaults,a||{});return this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline=nodeName=="div"||nodeName=="span";target.id||(this.uuid+=1,target.id="dp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),nodeName=="input"?this._connectDatepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newInst:function(a,b){var c=a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:b?bindHover($('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')):this.dpDiv}},_connectDatepicker:function(a,b){var c=$(a);b.append=$([]),b.trigger=$([]);c.hasClass(this.markerClassName)||(this._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.settings.disabled&&this._disableDatepicker(a))},_attachments:function(a,b){var c=this._get(b,"appendText"),d=this._get(b,"isRTL");b.append&&b.append.remove(),c&&(b.append=$('<span class="'+this._appendClass+'">'+c+"</span>"),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),b.trigger&&b.trigger.remove();var e=this._get(b,"showOn");(e=="focus"||e=="both")&&a.focus(this._showDatepicker);if(e=="button"||e=="both"){var f=this._get(b,"buttonText"),g=this._get(b,"buttonImage");b.trigger=$(this._get(b,"buttonImageOnly")?$("<img/>").addClass(this._triggerClass).attr({src:g,alt:f,title:f}):$('<button type="button"></button>').addClass(this._triggerClass).html(g==""?f:$("<img/>").attr({src:g,alt:f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){$.datepicker._datepickerShowing&&$.datepicker._lastInput==a[0]?$.datepicker._hideDatepicker():$.datepicker._showDatepicker(a[0]);return!1})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var d=function(a){var b=0,c=0;for(var d=0;d<a.length;d++)a[d].length>b&&(b=a[d].length,c=d);return c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=$(a);c.hasClass(this.markerClassName)||(c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._getDefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.settings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block"))},_dialogDatepicker:function(a,b,c,d,e){var f=this._dialogInst;if(!f){this.uuid+=1;var g="dp"+this.uuid;this._dialogInput=$('<input type="text" id="'+g+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>'),this._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInput),f=this._dialogInst=this._newInst(this._dialogInput,!1),f.settings={},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.settings,d||{}),b=b&&b.constructor==Date?this._formatDate(f,b):b,this._dialogInput.val(b),this._pos=e?e.length?e:[e.pageX,e.pageY]:null;if(!this._pos){var h=document.documentElement.clientWidth,i=document.documentElement.clientHeight,j=document.documentElement.scrollLeft||document.body.scrollLeft,k=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[h/2-100+j,i/2-150+k]}this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),f.settings.onSelect=c,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP_NAME,f);return this},_destroyDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();$.removeData(a,PROP_NAME),d=="input"?(c.append.remove(),c.trigger.remove(),b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):(d=="div"||d=="span")&&b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!1,c.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().removeClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b})}},_disableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!0,c.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().addClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b}),this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return!0;return!1},_getInst:function(a){try{return $.data(a,PROP_NAME)}catch(b){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(a,b,c){var d=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?$.extend({},$.datepicker._defaults):d?b=="all"?$.extend({},d.settings):this._get(d,b):null;var e=b||{};typeof b=="string"&&(e={},e[b]=c);if(d){this._curInst==d&&this._hideDatepicker();var f=this._getDateDatepicker(a,!0),g=this._getMinMaxDate(d,"min"),h=this._getMinMaxDate(d,"max");extendRemove(d.settings,e),g!==null&&e.dateFormat!==undefined&&e.minDate===undefined&&(d.settings.minDate=this._formatDate(d,g)),h!==null&&e.dateFormat!==undefined&&e.maxDate===undefined&&(d.settings.maxDate=this._formatDate(d,h)),this._attachments($(a),d),this._autoSize(d),this._setDate(d,f),this._updateAlternate(d),this._updateDatepicker(d)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){var b=this._getInst(a);b&&this._updateDatepicker(b)},_setDateDatepicker:function(a,b){var c=this._getInst(a);c&&(this._setDate(c,b),this._updateDatepicker(c),this._updateAlternate(c))},_getDateDatepicker:function(a,b){var c=this._getInst(a);c&&!c.inline&&this._setDateFromField(c,b);return c?this._getDate(c):null},_doKeyDown:function(a){var b=$.datepicker._getInst(a.target),c=!0,d=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=!0;if($.datepicker._datepickerShowing)switch(a.keyCode){case 9:$.datepicker._hideDatepicker(),c=!1;break;case 13:var e=$("td."+$.datepicker._dayOverClass+":not(."+$.datepicker._currentClass+")",b.dpDiv);e[0]&&$.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,e[0]);var f=$.datepicker._get(b,"onSelect");if(f){var g=$.datepicker._formatDate(b);f.apply(b.input?b.input[0]:null,[g,b])}else $.datepicker._hideDatepicker();return!1;case 27:$.datepicker._hideDatepicker();break;case 33:$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 34:$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 35:(a.ctrlKey||a.metaKey)&&$.datepicker._clearDate(a.target),c=a.ctrlKey||a.metaKey;break;case 36:(a.ctrlKey||a.metaKey)&&$.datepicker._gotoToday(a.target),c=a.ctrlKey||a.metaKey;break;case 37:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?1:-1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 38:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,-7,"D"),c=a.ctrlKey||a.metaKey;break;case 39:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?-1:1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 40:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,7,"D"),c=a.ctrlKey||a.metaKey;break;default:c=!1}else a.keyCode==36&&a.ctrlKey?$.datepicker._showDatepicker(this):c=!1;c&&(a.preventDefault(),a.stopPropagation())},_doKeyPress:function(a){var b=$.datepicker._getInst(a.target);if($.datepicker._get(b,"constrainInput")){var c=$.datepicker._possibleChars($.datepicker._get(b,"dateFormat")),d=String.fromCharCode(a.charCode==undefined?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||d<" "||!c||c.indexOf(d)>-1}},_doKeyUp:function(a){var b=$.datepicker._getInst(a.target);if(b.input.val()!=b.lastVal)try{var c=$.datepicker.parseDate($.datepicker._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._getFormatConfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._updateAlternate(b),$.datepicker._updateDatepicker(b))}catch(a){$.datepicker.log(a)}return!0},_showDatepicker:function(a){a=a.target||a,a.nodeName.toLowerCase()!="input"&&(a=$("input",a.parentNode)[0]);if(!$.datepicker._isDisabledDatepicker(a)&&$.datepicker._lastInput!=a){var b=$.datepicker._getInst(a);$.datepicker._curInst&&$.datepicker._curInst!=b&&($.datepicker._curInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.datepicker._hideDatepicker($.datepicker._curInst.input[0]));var c=$.datepicker._get(b,"beforeShow"),d=c?c.apply(a,[a,b]):{};if(d===!1)return;extendRemove(b.settings,d),b.lastVal=null,$.datepicker._lastInput=a,$.datepicker._setDateFromField(b),$.datepicker._inDialog&&(a.value=""),$.datepicker._pos||($.datepicker._pos=$.datepicker._findPos(a),$.datepicker._pos[1]+=a.offsetHeight);var e=!1;$(a).parents().each(function(){e|=$(this).css("position")=="fixed";return!e}),e&&$.browser.opera&&($.datepicker._pos[0]-=document.documentElement.scrollLeft,$.datepicker._pos[1]-=document.documentElement.scrollTop);var f={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null,b.dpDiv.empty(),b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(b),f=$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.datepicker._inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",left:f.left+"px",top:f.top+"px"});if(!b.inline){var g=$.datepicker._get(b,"showAnim"),h=$.datepicker._get(b,"duration"),i=function(){var a=b.dpDiv.find("iframe.ui-datepicker-cover");if(!!a.length){var c=$.datepicker._getBorders(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepicker._datepickerShowing=!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datepicker._get(b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i(),b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this;b.maxRows=4;var c=$.datepicker._getBorders(a.dpDiv);instActive=a,a.dpDiv.empty().append(this._generateHTML(a));var d=a.dpDiv.find("iframe.ui-datepicker-cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOverClass+" a").mouseover();var e=this._getNumberOfMonths(a),f=e[1],g=17;a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",g*f+"em"),a.dpDiv[(e[0]!=1||e[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),a==$.datepicker._curInst&&$.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var h=a.yearshtml;setTimeout(function(){h===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml),h=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(a){return{thin:1,medium:2,thick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var d=a.dpDiv.outerWidth(),e=a.dpDiv.outerHeight(),f=a.input?a.input.outerWidth():0,g=a.input?a.input.outerHeight():0,h=document.documentElement.clientWidth+$(document).scrollLeft(),i=document.documentElement.clientHeight+$(document).scrollTop();b.left-=this._get(a,"isRTL")?d-f:0,b.left-=c&&b.left==a.input.offset().left?$(document).scrollLeft():0,b.top-=c&&b.top==a.input.offset().top+g?$(document).scrollTop():0,b.left-=Math.min(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),b.top-=Math.min(b.top,b.top+e>i&&i>e?Math.abs(e+g):0);return b},_findPos:function(a){var b=this._getInst(a),c=this._get(b,"isRTL");while(a&&(a.type=="hidden"||a.nodeType!=1||$.expr.filters.hidden(a)))a=a[c?"previousSibling":"nextSibling"];var d=$(a).offset();return[d.left,d.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=$.data(a,PROP_NAME))&&this._datepickerShowing){var c=this._get(b,"showAnim"),d=this._get(b,"duration"),e=this,f=function(){$.datepicker._tidyDialog(b),e._curInst=null};$.effects&&$.effects[c]?b.dpDiv.hide(c,$.datepicker._get(b,"showOptions"),d,f):b.dpDiv[c=="slideDown"?"slideUp":c=="fadeIn"?"fadeOut":"hide"](c?d:null,f),c||f(),this._datepickerShowing=!1;var g=this._get(b,"onClose");g&&g.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=!1}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(!!$.datepicker._curInst){var b=$(a.target),c=$.datepicker._getInst(b[0]);(b[0].id!=$.datepicker._mainDivId&&b.parents("#"+$.datepicker._mainDivId).length==0&&!b.hasClass($.datepicker.markerClassName)&&!b.hasClass($.datepicker._triggerClass)&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog||!$.blockUI)||b.hasClass($.datepicker.markerClassName)&&$.datepicker._curInst!=c)&&$.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){var d=$(a),e=this._getInst(d[0]);this._isDisabledDatepicker(d[0])||(this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c),this._updateDatepicker(e))},_gotoToday:function(a){var b=$(a),c=this._getInst(b[0]);if(this._get(c,"gotoCurrent")&&c.currentDay)c.selectedDay=c.currentDay,c.drawMonth=c.selectedMonth=c.currentMonth,c.drawYear=c.selectedYear=c.currentYear;else{var d=new Date;c.selectedDay=d.getDate(),c.drawMonth=c.selectedMonth=d.getMonth(),c.drawYear=c.selectedYear=d.getFullYear()}this._notifyChange(c),this._adjustDate(b)},_selectMonthYear:function(a,b,c){var d=$(a),e=this._getInst(d[0]);e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10),this._notifyChange(e),this._adjustDate(d)},_selectDay:function(a,b,c,d){var e=$(a);if(!$(d).hasClass(this._unselectableClass)&&!this._isDisabledDatepicker(e[0])){var f=this._getInst(e[0]);f.selectedDay=f.currentDay=$("a",d).html(),f.selectedMonth=f.currentMonth=b,f.selectedYear=f.currentYear=c,this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){var b=$(a),c=this._getInst(b[0]);this._selectDate(b,"")},_selectDate:function(a,b){var c=$(a),d=this._getInst(c[0]);b=b!=null?b:this._formatDate(d),d.input&&d.input.val(b),this._updateAlternate(d);var e=this._get(d,"onSelect");e?e.apply(d.input?d.input[0]:null,[b,d]):d.input&&d.input.trigger("change"),d.inline?this._updateDatepicker(d):(this._hideDatepicker(),this._lastInput=d.input[0],typeof d.input[0]!="object"&&d.input.focus(),this._lastInput=null)},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),d=this._getDate(a),e=this.formatDate(c,d,this._getFormatConfig(a));$(b).each(function(){$(this).val(e)})}},noWeekends:function(a){var b=a.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b=new Date(a.getTime());b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();b.setMonth(0),b.setDate(1);return Math.floor(Math.round((c-b)/864e5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var d=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;d=typeof d!="string"?d:(new Date).getFullYear()%100+parseInt(d,10);var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,g=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,h=(c?c.monthNames:null)||this._defaults.monthNames,i=-1,j=-1,k=-1,l=-1,m=!1,n=function(b){var c=s+1<a.length&&a.charAt(s+1)==b;c&&s++;return c},o=function(a){var c=n(a),d=a=="@"?14:a=="!"?20:a=="y"&&c?4:a=="o"?3:2,e=new RegExp("^\\d{1,"+d+"}"),f=b.substring(r).match(e);if(!f)throw"Missing number at position "+r;r+=f[0].length;return parseInt(f[0],10)},p=function(a,c,d){var e=$.map(n(a)?d:c,function(a,b){return[[b,a]]}).sort(function(a,b){return-(a[1].length-b[1].length)}),f=-1;$.each(e,function(a,c){var d=c[1];if(b.substr(r,d.length).toLowerCase()==d.toLowerCase()){f=c[0],r+=d.length;return!1}});if(f!=-1)return f+1;throw"Unknown name at position "+r},q=function(){if(b.charAt(r)!=a.charAt(s))throw"Unexpected literal at position "+r;r++},r=0;for(var s=0;s<a.length;s++)if(m)a.charAt(s)=="'"&&!n("'")?m=!1:q();else switch(a.charAt(s)){case"d":k=o("d");break;case"D":p("D",e,f);break;case"o":l=o("o");break;case"m":j=o("m");break;case"M":j=p("M",g,h);break;case"y":i=o("y");break;case"@":var t=new Date(o("@"));i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"!":var t=new Date((o("!")-this._ticksTo1970)/1e4);i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"'":n("'")?q():m=!0;break;default:q()}if(r<b.length)throw"Extra/unparsed characters found in date: "+b.substring(r);i==-1?i=(new Date).getFullYear():i<100&&(i+=(new Date).getFullYear()-(new Date).getFullYear()%100+(i<=d?0:-100));if(l>-1){j=1,k=l;for(;;){var u=this._getDaysInMonth(i,j-1);if(k<=u)break;j++,k-=u}}var t=this._daylightSavingAdjust(new Date(i,j-1,k));if(t.getFullYear()!=i||t.getMonth()+1!=j||t.getDate()!=k)throw"Invalid date";return t},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,e=(c?c.dayNames:null)||this._defaults.dayNames,f=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,h=function(b){var c=m+1<a.length&&a.charAt(m+1)==b;c&&m++;return c},i=function(a,b,c){var d=""+b;if(h(a))while(d.length<c)d="0"+d;return d},j=function(a,b,c,d){return h(a)?d[b]:c[b]},k="",l=!1;if(b)for(var m=0;m<a.length;m++)if(l)a.charAt(m)=="'"&&!h("'")?l=!1:k+=a.charAt(m);else switch(a.charAt(m)){case"d":k+=i("d",b.getDate(),2);break;case"D":k+=j("D",b.getDay(),d,e);break;case"o":k+=i("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864e5),3);break;case"m":k+=i("m",b.getMonth()+1,2);break;case"M":k+=j("M",b.getMonth(),f,g);break;case"y":k+=h("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case"@":k+=b.getTime();break;case"!":k+=b.getTime()*1e4+this._ticksTo1970;break;case"'":h("'")?k+="'":l=!0;break;default:k+=a.charAt(m)}return k},_possibleChars:function(a){var b="",c=!1,d=function(b){var c=e+1<a.length&&a.charAt(e+1)==b;c&&e++;return c};for(var e=0;e<a.length;e++)if(c)a.charAt(e)=="'"&&!d("'")?c=!1:b+=a.charAt(e);else switch(a.charAt(e)){case"d":case"m":case"y":case"@":b+="0123456789";break;case"D":case"M":return null;case"'":d("'")?b+="'":c=!0;break;default:b+=a.charAt(e)}return b},_get:function(a,b){return a.settings[b]!==undefined?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),d=a.lastVal=a.input?a.input.val():null,e,f;e=f=this._getDefaultDate(a);var g=this._getFormatConfig(a);try{e=this.parseDate(c,d,g)||f}catch(h){this.log(h),d=b?"":d}a.selectedDay=e.getDate(),a.drawMonth=a.selectedMonth=e.getMonth(),a.drawYear=a.selectedYear=e.getFullYear(),a.currentDay=d?e.getDate():0,a.currentMonth=d?e.getMonth():0,a.currentYear=d?e.getFullYear():0,this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var d=function(a){var b=new Date;b.setDate(b.getDate()+a);return b},e=function(b){try{return $.datepicker.parseDate($.datepicker._get(a,"dateFormat"),b,$.datepicker._getFormatConfig(a))}catch(c){}var d=(b.toLowerCase().match(/^c/)?$.datepicker._getDate(a):null)||new Date,e=d.getFullYear(),f=d.getMonth(),g=d.getDate(),h=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,i=h.exec(b);while(i){switch(i[2]||"d"){case"d":case"D":g+=parseInt(i[1],10);break;case"w":case"W":g+=parseInt(i[1],10)*7;break;case"m":case"M":f+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f));break;case"y":case"Y":e+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f))}i=h.exec(b)}return new Date(e,f,g)},f=b==null||b===""?c:typeof b=="string"?e(b):typeof b=="number"?isNaN(b)?c:d(b):new Date(b.getTime());f=f&&f.toString()=="Invalid Date"?c:f,f&&(f.setHours(0),f.setMinutes(0),f.setSeconds(0),f.setMilliseconds(0));return this._daylightSavingAdjust(f)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var d=!b,e=a.selectedMonth,f=a.selectedYear,g=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=g.getDate(),a.drawMonth=a.selectedMonth=a.currentMonth=g.getMonth(),a.drawYear=a.selectedYear=a.currentYear=g.getFullYear(),(e!=a.selectedMonth||f!=a.selectedYear)&&!c&&this._notifyChange(a),this._adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return b},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),d=this._get(a,"showButtonPanel"),e=this._get(a,"hideIfNoPrevNext"),f=this._get(a,"navigationAsDateFormat"),g=this._getNumberOfMonths(a),h=this._get(a,"showCurrentAtPos"),i=this._get(a,"stepMonths"),j=g[0]!=1||g[1]!=1,k=this._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),l=this._getMinMaxDate(a,"min"),m=this._getMinMaxDate(a,"max"),n=a.drawMonth-h,o=a.drawYear;n<0&&(n+=12,o--);if(m){var p=this._daylightSavingAdjust(new Date(m.getFullYear(),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=l&&p<l?l:p;while(this._daylightSavingAdjust(new Date(o,n,1))>p)n--,n<0&&(n=11,o--)}a.drawMonth=n,a.drawYear=o;var q=this._get(a,"prevText");q=f?this.formatDate(q,this._daylightSavingAdjust(new Date(o,n-i,1)),this._getFormatConfig(a)):q;var r=this._canAdjustMonth(a,-1,o,n)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', -"+i+", 'M');\""+' title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>":e?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>",s=this._get(a,"nextText");s=f?this.formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1)),this._getFormatConfig(a)):s;var t=this._canAdjustMonth(a,1,o,n)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', +"+i+", 'M');\""+' title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":e?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>",u=this._get(a,"currentText"),v=this._get(a,"gotoCurrent")&&a.currentDay?k:b;u=f?this.formatDate(u,v,this._getFormatConfig(a)):u;var w=a.inline?"":'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+dpuuid+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>",x=d?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?w:"")+(this._isInRange(a,v)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._gotoToday('#"+a.id+"');\""+">"+u+"</button>":"")+(c?"":w)+"</div>":"",y=parseInt(this._get(a,"firstDay"),10);y=isNaN(y)?0:y;var z=this._get(a,"showWeek"),A=this._get(a,"dayNames"),B=this._get(a,"dayNamesShort"),C=this._get(a,"dayNamesMin"),D=this._get(a,"monthNames"),E=this._get(a,"monthNamesShort"),F=this._get(a,"beforeShowDay"),G=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths"),I=this._get(a,"calculateWeek")||this.iso8601Week,J=this._getDefaultDate(a),K="";for(var L=0;L<g[0];L++){var M="";this.maxRows=4;for(var N=0;N<g[1];N++){var O=this._daylightSavingAdjust(new Date(o,n,a.selectedDay)),P=" ui-corner-all",Q="";if(j){Q+='<div class="ui-datepicker-group';if(g[1]>1)switch(N){case 0:Q+=" ui-datepicker-group-first",P=" ui-corner-"+(c?"right":"left");break;case g[1]-1:Q+=" ui-datepicker-group-last",P=" ui-corner-"+(c?"left":"right");break;default:Q+=" ui-datepicker-group-middle",P=""}Q+='">'}Q+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+P+'">'+(/all|left/.test(P)&&L==0?c?t:r:"")+(/all|right/.test(P)&&L==0?c?r:t:"")+this._generateMonthYearHeader(a,n,o,l,m,L>0||N>0,D,E)+'</div><table class="ui-datepicker-calendar"><thead>'+"<tr>";var R=z?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(var S=0;S<7;S++){var T=(S+y)%7;R+="<th"+((S+y+6)%7>=5?' class="ui-datepicker-week-end"':"")+">"+'<span title="'+A[T]+'">'+C[T]+"</span></th>"}Q+=R+"</tr></thead><tbody>";var U=this._getDaysInMonth(o,n);o==a.selectedYear&&n==a.selectedMonth&&(a.selectedDay=Math.min(a.selectedDay,U));var V=(this._getFirstDayOfMonth(o,n)-y+7)%7,W=Math.ceil((V+U)/7),X=j?this.maxRows>W?this.maxRows:W:W;this.maxRows=X;var Y=this._daylightSavingAdjust(new Date(o,n,1-V));for(var Z=0;Z<X;Z++){Q+="<tr>";var _=z?'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(Y)+"</td>":"";for(var S=0;S<7;S++){var ba=F?F.apply(a.input?a.input[0]:null,[Y]):[!0,""],bb=Y.getMonth()!=n,bc=bb&&!H||!ba[0]||l&&Y<l||m&&Y>m;_+='<td class="'+((S+y+6)%7>=5?" ui-datepicker-week-end":"")+(bb?" ui-datepicker-other-month":"")+(Y.getTime()==O.getTime()&&n==a.selectedMonth&&a._keyEvent||J.getTime()==Y.getTime()&&J.getTime()==O.getTime()?" "+this._dayOverClass:"")+(bc?" "+this._unselectableClass+" ui-state-disabled":"")+(bb&&!G?"":" "+ba[1]+(Y.getTime()==k.getTime()?" "+this._currentClass:"")+(Y.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!bb||G)&&ba[2]?' title="'+ba[2]+'"':"")+(bc?"":' onclick="DP_jQuery_'+dpuuid+".datepicker._selectDay('#"+a.id+"',"+Y.getMonth()+","+Y.getFullYear()+', this);return false;"')+">"+(bb&&!G?"&#xa0;":bc?'<span class="ui-state-default">'+Y.getDate()+"</span>":'<a class="ui-state-default'+(Y.getTime()==b.getTime()?" ui-state-highlight":"")+(Y.getTime()==k.getTime()?" ui-state-active":"")+(bb?" ui-priority-secondary":"")+'" href="#">'+Y.getDate()+"</a>")+"</td>",Y.setDate(Y.getDate()+1),Y=this._daylightSavingAdjust(Y)}Q+=_+"</tr>"}n++,n>11&&(n=0,o++),Q+="</tbody></table>"+(j?"</div>"+(g[0]>0&&N==g[1]-1?'<div class="ui-datepicker-row-break"></div>':""):""),M+=Q}K+=M}K+=x+($.browser.msie&&parseInt($.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':""),a._keyEvent=!1;return K},_generateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i=this._get(a,"changeMonth"),j=this._get(a,"changeYear"),k=this
193
+ ._get(a,"showMonthAfterYear"),l='<div class="ui-datepicker-title">',m="";if(f||!i)m+='<span class="ui-datepicker-month">'+g[b]+"</span>";else{var n=d&&d.getFullYear()==c,o=e&&e.getFullYear()==c;m+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" "+">";for(var p=0;p<12;p++)(!n||p>=d.getMonth())&&(!o||p<=e.getMonth())&&(m+='<option value="'+p+'"'+(p==b?' selected="selected"':"")+">"+h[p]+"</option>");m+="</select>"}k||(l+=m+(f||!i||!j?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(f||!j)l+='<span class="ui-datepicker-year">'+c+"</span>";else{var q=this._get(a,"yearRange").split(":"),r=(new Date).getFullYear(),s=function(a){var b=a.match(/c[+-].*/)?c+parseInt(a.substring(1),10):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return isNaN(b)?r:b},t=s(q[0]),u=Math.max(t,s(q[1]||""));t=d?Math.max(t,d.getFullYear()):t,u=e?Math.min(u,e.getFullYear()):u,a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" "+">";for(;t<=u;t++)a.yearshtml+='<option value="'+t+'"'+(t==c?' selected="selected"':"")+">"+t+"</option>";a.yearshtml+="</select>",l+=a.yearshtml,a.yearshtml=null}}l+=this._get(a,"yearSuffix"),k&&(l+=(f||!i||!j?"&#xa0;":"")+m),l+="</div>";return l},_adjustInstDate:function(a,b,c){var d=a.drawYear+(c=="Y"?b:0),e=a.drawMonth+(c=="M"?b:0),f=Math.min(a.selectedDay,this._getDaysInMonth(d,e))+(c=="D"?b:0),g=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=g.getDate(),a.drawMonth=a.selectedMonth=g.getMonth(),a.drawYear=a.selectedYear=g.getFullYear(),(c=="M"||c=="Y")&&this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max"),e=c&&b<c?c:b;e=d&&e>d?d:e;return e},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b=this._get(a,"numberOfMonths");return b==null?[1,1]:typeof b=="number"?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,d){var e=this._getNumberOfMonths(a),f=this._daylightSavingAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));b<0&&f.setDate(this._getDaysInMonth(f.getFullYear(),f.getMonth()));return this._isInRange(a,f)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!d||b.getTime()<=d.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=a.selectedDay,a.currentMonth=a.selectedMonth,a.currentYear=a.selectedYear);var e=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConfig(a))}}),$.fn.datepicker=function(a){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv),$.datepicker.initialized=!0);var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return $.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return $.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b));return this.each(function(){typeof a=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this].concat(b)):$.datepicker._attachDatepicker(this,a)})},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.8.17",window["DP_jQuery_"+dpuuid]=$})(jQuery);/*
194
+ * jQuery UI Progressbar 1.8.17
195
+ *
196
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
197
+ * Dual licensed under the MIT or GPL Version 2 licenses.
198
+ * http://jquery.org/license
199
+ *
200
+ * http://docs.jquery.com/UI/Progressbar
201
+ *
202
+ * Depends:
203
+ * jquery.ui.core.js
204
+ * jquery.ui.widget.js
205
+ */(function(a,b){a.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()}),this.valueDiv=a("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this.oldValue=this._value(),this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove(),a.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===b)return this._value();this._setOption("value",a);return this},_setOption:function(b,c){b==="value"&&(this.options.value=c,this._refreshValue(),this._value()===this.options.max&&this._trigger("complete")),a.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;typeof a!="number"&&(a=0);return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*this._value()/this.options.max},_refreshValue:function(){var a=this.value(),b=this._percentage();this.oldValue!==a&&(this.oldValue=a,this._trigger("change")),this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(b.toFixed(0)+"%"),this.element.attr("aria-valuenow",a)}}),a.extend(a.ui.progressbar,{version:"1.8.17"})})(jQuery);/*
206
+ * jQuery UI Effects 1.8.17
207
+ *
208
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
209
+ * Dual licensed under the MIT or GPL Version 2 licenses.
210
+ * http://jquery.org/license
211
+ *
212
+ * http://docs.jquery.com/UI/Effects/
213
+ */jQuery.effects||function(a,b){function l(b){if(!b||typeof b=="number"||a.fx.speeds[b])return!0;if(typeof b=="string"&&!a.effects[b])return!0;return!1}function k(b,c,d,e){typeof b=="object"&&(e=c,d=null,c=b,b=c.effect),a.isFunction(c)&&(e=c,d=null,c={});if(typeof c=="number"||a.fx.speeds[c])e=d,d=c,c={};a.isFunction(d)&&(e=d,d=null),c=c||{},d=d||c.duration,d=a.fx.off?0:typeof d=="number"?d:d in a.fx.speeds?a.fx.speeds[d]:a.fx.speeds._default,e=e||c.complete;return[b,c,d,e]}function j(a,b){var c={_:0},d;for(d in b)a[d]!=b[d]&&(c[d]=b[d]);return c}function i(b){var c,d;for(c in b)d=b[c],(d==null||a.isFunction(d)||c in g||/scrollbar/.test(c)||!/color/i.test(c)&&isNaN(parseFloat(d)))&&delete b[c];return b}function h(){var a=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,b={},c,d;if(a&&a.length&&a[0]&&a[a[0]]){var e=a.length;while(e--)c=a[e],typeof a[c]=="string"&&(d=c.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()}),b[d]=a[c])}else for(c in a)typeof a[c]=="string"&&(b[c]=a[c]);return b}function d(b,d){var e;do{e=a.curCSS(b,d);if(e!=""&&e!="transparent"||a.nodeName(b,"body"))break;d="backgroundColor"}while(b=b.parentNode);return c(e)}function c(b){var c;if(b&&b.constructor==Array&&b.length==3)return b;if(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(c[1],10),parseInt(c[2],10),parseInt(c[3],10)];if(c=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(c[1])*2.55,parseFloat(c[2])*2.55,parseFloat(c[3])*2.55];if(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)];if(c=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)];if(c=/rgba\(0, 0, 0, 0\)/.exec(b))return e.transparent;return e[a.trim(b).toLowerCase()]}a.effects={},a.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","borderColor","color","outlineColor"],function(b,e){a.fx.step[e]=function(a){a.colorInit||(a.start=d(a.elem,e),a.end=c(a.end),a.colorInit=!0),a.elem.style[e]="rgb("+Math.max(Math.min(parseInt(a.pos*(a.end[0]-a.start[0])+a.start[0],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[1]-a.start[1])+a.start[1],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[2]-a.start[2])+a.start[2],10),255),0)+")"}});var e={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},f=["add","remove","toggle"],g={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};a.effects.animateClass=function(b,c,d,e){a.isFunction(d)&&(e=d,d=null);return this.queue(function(){var g=a(this),k=g.attr("style")||" ",l=i(h.call(this)),m,n=g.attr("class");a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),m=i(h.call(this)),g.attr("class",n),g.animate(j(l,m),{queue:!1,duration:c,easing:d,complete:function(){a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),typeof g.attr("style")=="object"?(g.attr("style").cssText="",g.attr("style").cssText=k):g.attr("style",k),e&&e.apply(this,arguments),a.dequeue(this)}})})},a.fn.extend({_addClass:a.fn.addClass,addClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{add:b},c,d,e]):this._addClass(b)},_removeClass:a.fn.removeClass,removeClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{remove:b},c,d,e]):this._removeClass(b)},_toggleClass:a.fn.toggleClass,toggleClass:function(c,d,e,f,g){return typeof d=="boolean"||d===b?e?a.effects.animateClass.apply(this,[d?{add:c}:{remove:c},e,f,g]):this._toggleClass(c,d):a.effects.animateClass.apply(this,[{toggle:c},d,e,f])},switchClass:function(b,c,d,e,f){return a.effects.animateClass.apply(this,[{add:c,remove:b},d,e,f])}}),a.extend(a.effects,{version:"1.8.17",save:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.data("ec.storage."+b[c],a[0].style[b[c]])},restore:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.css(b[c],a.data("ec.storage."+b[c]))},setMode:function(a,b){b=="toggle"&&(b=a.is(":hidden")?"show":"hide");return b},getBaseline:function(a,b){var c,d;switch(a[0]){case"top":c=0;break;case"middle":c=.5;break;case"bottom":c=1;break;default:c=a[0]/b.height}switch(a[1]){case"left":d=0;break;case"center":d=.5;break;case"right":d=1;break;default:d=a[1]/b.width}return{x:d,y:c}},createWrapper:function(b){if(b.parent().is(".ui-effects-wrapper"))return b.parent();var c={width:b.outerWidth(!0),height:b.outerHeight(!0),"float":b.css("float")},d=a("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"}));return d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;if(b.parent().is(".ui-effects-wrapper")){c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus();return c}return b},setTransition:function(b,c,d,e){e=e||{},a.each(c,function(a,c){unit=b.cssUnit(c),unit[0]>0&&(e[c]=unit[0]*d+unit[1])});return e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];if(a.fx.off||!i)return h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)});return i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="show";return this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="hide";return this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);c[1].mode="toggle";return this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])});return d}}),a.easing.jswing=a.easing.swing,a.extend(a.easing,{def:"easeOutQuad",swing:function(b,c,d,e,f){return a.easing[a.easing.def](b,c,d,e,f)},easeInQuad:function(a,b,c,d,e){return d*(b/=e)*b+c},easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c},easeInOutQuad:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b+c;return-d/2*(--b*(b-2)-1)+c},easeInCubic:function(a,b,c,d,e){return d*(b/=e)*b*b+c},easeOutCubic:function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c},easeInOutCubic:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b+c;return d/2*((b-=2)*b*b+2)+c},easeInQuart:function(a,b,c,d,e){return d*(b/=e)*b*b*b+c},easeOutQuart:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c},easeInOutQuart:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b+c;return-d/2*((b-=2)*b*b*b-2)+c},easeInQuint:function(a,b,c,d,e){return d*(b/=e)*b*b*b*b+c},easeOutQuint:function(a,b,c,d,e){return d*((b=b/e-1)*b*b*b*b+1)+c},easeInOutQuint:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b*b+c;return d/2*((b-=2)*b*b*b*b+2)+c},easeInSine:function(a,b,c,d,e){return-d*Math.cos(b/e*(Math.PI/2))+d+c},easeOutSine:function(a,b,c,d,e){return d*Math.sin(b/e*(Math.PI/2))+c},easeInOutSine:function(a,b,c,d,e){return-d/2*(Math.cos(Math.PI*b/e)-1)+c},easeInExpo:function(a,b,c,d,e){return b==0?c:d*Math.pow(2,10*(b/e-1))+c},easeOutExpo:function(a,b,c,d,e){return b==e?c+d:d*(-Math.pow(2,-10*b/e)+1)+c},easeInOutExpo:function(a,b,c,d,e){if(b==0)return c;if(b==e)return c+d;if((b/=e/2)<1)return d/2*Math.pow(2,10*(b-1))+c;return d/2*(-Math.pow(2,-10*--b)+2)+c},easeInCirc:function(a,b,c,d,e){return-d*(Math.sqrt(1-(b/=e)*b)-1)+c},easeOutCirc:function(a,b,c,d,e){return d*Math.sqrt(1-(b=b/e-1)*b)+c},easeInOutCirc:function(a,b,c,d,e){if((b/=e/2)<1)return-d/2*(Math.sqrt(1-b*b)-1)+c;return d/2*(Math.sqrt(1-(b-=2)*b)+1)+c},easeInElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g))+c},easeOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*b)*Math.sin((b*e-f)*2*Math.PI/g)+d+c},easeInOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e/2)==2)return c+d;g||(g=e*.3*1.5);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);if(b<1)return-0.5*h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)+c;return h*Math.pow(2,-10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)*.5+d+c},easeInBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*(c/=f)*c*((g+1)*c-g)+d},easeOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*((c=c/f-1)*c*((g+1)*c+g)+1)+d},easeInOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);if((c/=f/2)<1)return e/2*c*c*(((g*=1.525)+1)*c-g)+d;return e/2*((c-=2)*c*(((g*=1.525)+1)*c+g)+2)+d},easeInBounce:function(b,c,d,e,f){return e-a.easing.easeOutBounce(b,f-c,0,e,f)+d},easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?d*7.5625*b*b+c:b<2/2.75?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:b<2.5/2.75?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c},easeInOutBounce:function(b,c,d,e,f){if(c<f/2)return a.easing.easeInBounce(b,c*2,0,e,f)*.5+d;return a.easing.easeOutBounce(b,c*2-f,0,e,f)*.5+e*.5+d}})}(jQuery);/*
214
+ * jQuery UI Effects Blind 1.8.17
215
+ *
216
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
217
+ * Dual licensed under the MIT or GPL Version 2 licenses.
218
+ * http://jquery.org/license
219
+ *
220
+ * http://docs.jquery.com/UI/Effects/Blind
221
+ *
222
+ * Depends:
223
+ * jquery.effects.core.js
224
+ */(function(a,b){a.effects.blind=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=f=="vertical"?"height":"width",i=f=="vertical"?g.height():g.width();e=="show"&&g.css(h,0);var j={};j[h]=e=="show"?i:0,g.animate(j,b.duration,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);/*
225
+ * jQuery UI Effects Bounce 1.8.17
226
+ *
227
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
228
+ * Dual licensed under the MIT or GPL Version 2 licenses.
229
+ * http://jquery.org/license
230
+ *
231
+ * http://docs.jquery.com/UI/Effects/Bounce
232
+ *
233
+ * Depends:
234
+ * jquery.effects.core.js
235
+ */(function(a,b){a.effects.bounce=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"up",g=b.options.distance||20,h=b.options.times||5,i=b.duration||250;/show|hide/.test(e)&&d.push("opacity"),a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",g=b.options.distance||(j=="top"?c.outerHeight({margin:!0})/3:c.outerWidth({margin:!0})/3);e=="show"&&c.css("opacity",0).css(j,k=="pos"?-g:g),e=="hide"&&(g=g/(h*2)),e!="hide"&&h--;if(e=="show"){var l={opacity:1};l[j]=(k=="pos"?"+=":"-=")+g,c.animate(l,i/2,b.options.easing),g=g/2,h--}for(var m=0;m<h;m++){var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing),g=e=="hide"?g*2:g/2}if(e=="hide"){var l={opacity:0};l[j]=(k=="pos"?"-=":"+=")+g,c.animate(l,i/2,b.options.easing,function(){c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}else{var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);/*
236
+ * jQuery UI Effects Clip 1.8.17
237
+ *
238
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
239
+ * Dual licensed under the MIT or GPL Version 2 licenses.
240
+ * http://jquery.org/license
241
+ *
242
+ * http://docs.jquery.com/UI/Effects/Clip
243
+ *
244
+ * Depends:
245
+ * jquery.effects.core.js
246
+ */(function(a,b){a.effects.clip=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","height","width"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical"